第3章:Widget体系与布局原理
3.1 Widget树的构建与渲染机制
3.1.1 Flutter渲染流水线解析
Flutter的渲染系统是其高性能的核心所在。理解渲染机制对于优化应用性能至关重要。
// Flutter渲染流水线的三棵树结构示例
class RenderingPipelineDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Widget Tree - 配置信息
return Scaffold(
appBar: AppBar(title: Text('渲染流水线演示')),
body: Container( // Widget节点
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Text('Hello Flutter'), // Widget节点
ElevatedButton(
onPressed: () {},
child: Text('点击按钮'),
),
],
),
),
);
}
}
// Element Tree对应的概念示例
/*
Widget Tree Element Tree RenderObject Tree
----------- -------------- -------------------
Scaffold → ScaffoldElement → RenderScaffold
├─AppBar → ├─AppBarElement → ├─RenderAppBar
└─Container → └─ContainerElement → └─RenderContainer
└─Column → └─ColumnElement → └─RenderFlex
├─Text → ├─TextElement → ├─RenderParagraph
└─Button → └─ButtonElement → └─RenderButton
*/
3.1.2 Widget树的构建过程
// 演示Widget树构建的详细过程
class WidgetTreeBuildDemo extends StatefulWidget {
@override
_WidgetTreeBuildDemoState createState() => _WidgetTreeBuildDemoState();
}
class _WidgetTreeBuildDemoState extends State<WidgetTreeBuildDemo> {
int _counter = 0;
@override
Widget build(BuildContext context) {
print('🔄 build方法被调用 - counter: $_counter');
return Scaffold(
appBar: AppBar(
title: Text('Widget树构建演示'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 每次build都会创建新的Text Widget
Text(
'计数器值:$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
SizedBox(height: 20),
// 但Element树和RenderObject树会被复用
ElevatedButton(
onPressed: _incrementCounter,
child: Text('增加计数'),
),
],
),
),
);
}
void _incrementCounter() {
setState(() {
_counter++;
// setState触发Widget树重建,但Element树只会更新必要部分
});
}
@override
void dispose() {
print('🗑️ Widget被销毁');
super.dispose();
}
}
3.1.3 渲染性能优化原理
// 性能优化的最佳实践
class PerformanceOptimizedWidget extends StatefulWidget {
@override
_PerformanceOptimizedWidgetState createState() =>
_PerformanceOptimizedWidgetState();
}
class _PerformanceOptimizedWidgetState extends State<PerformanceOptimizedWidget> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('性能优化演示')),
body: Column(
children: [
// ❌ 错误做法:每次build都创建新的expensive widget
// ExpensiveWidget(),
// ✅ 正确做法1:提取为常量
const ExpensiveStaticWidget(),
// ✅ 正确做法2:使用const构造函数
const Padding(
padding: EdgeInsets.all(16.0),
child: Text('这是一个常量Widget'),
),
// ✅ 正确做法3:条件性重建
CounterDisplay(counter: _counter),
// ✅ 正确做法4:使用Builder减少重建范围
Builder(
builder: (context) => ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('增加计数'),
),
),
],
),
);
}
}
// 独立的计数器显示组件,只有当counter改变时才重建
class CounterDisplay extends StatelessWidget {
final int counter;
const CounterDisplay({Key? key, required this.counter}) : super(key: key);
@override
Widget build(BuildContext context) {
print('🔄 CounterDisplay重建 - counter: $counter');
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(8),
),
child: Text(
'当前计数:$counter',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
);
}
}
// 昂贵的静态Widget - 使用const避免重建
class ExpensiveStaticWidget extends StatelessWidget {
const ExpensiveStaticWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('💰 ExpensiveStaticWidget被构建(应该只出现一次)');
return Container(
height: 200,
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) => ListTile(
title: Text('静态项目 $index'),
),
),
);
}
}
3.2 StatelessWidget与StatefulWidget详解
3.2.1 StatelessWidget深入理解
// StatelessWidget的完整实现示例
class CustomStatelessWidget extends StatelessWidget {
final String title;
final Color backgroundColor;
final VoidCallback? onTap;
// 构造函数 - 所有属性都应该是final
const CustomStatelessWidget({
Key? key,
required this.title,
this.backgroundColor = Colors.blue,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// build方法是纯函数,相同输入总是产生相同输出
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
);
}
// 重写操作符以支持比较
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CustomStatelessWidget &&
runtimeType == other.runtimeType &&
title == other.title &&
backgroundColor == other.backgroundColor;
@override
int get hashCode => title.hashCode ^ backgroundColor.hashCode;
}
// StatelessWidget的最佳实践示例
class UserProfileCard extends StatelessWidget {
final User user;
final VoidCallback? onEdit;
const UserProfileCard({
Key? key,
required this.user,
this.onEdit,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(8),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 用户头像和基本信息
Row(
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(user.avatarUrl),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user.name,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
user.email,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
if (onEdit != null)
IconButton(
icon: Icon(Icons.edit),
onPressed: onEdit,
),
],
),
SizedBox(height: 12),
// 用户描述
if (user.bio.isNotEmpty)
Text(
user.bio,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
);
}
}
// User模型类
class User {
final String name;
final String email;
final String avatarUrl;
final String bio;
const User({
required this.name,
required this.email,
required this.avatarUrl,
this.bio = '',
});
}
3.2.2 StatefulWidget完整生命周期
// StatefulWidget完整生命周期演示
class LifecycleDemo extends StatefulWidget {
final String title;
const LifecycleDemo({Key? key, required this.title}) : super(key: key);
@override
_LifecycleDemoState createState() {
print('📱 1. createState() - 创建State对象');
return _LifecycleDemoState();
}
}
class _LifecycleDemoState extends State<LifecycleDemo>
with WidgetsBindingObserver {
int _counter = 0;
late String _title;
// 2. 构造函数
_LifecycleDemoState() {
print('🏗️ 2. State构造函数');
}
// 3. initState - 只调用一次
@override
void initState() {
super.initState();
print('🎬 3. initState() - 初始化状态');
_title = widget.title;
// 添加生命周期观察者
WidgetsBinding.instance.addObserver(this);
// 异步初始化
_asyncInit();
}
Future<void> _asyncInit() async {
// 模拟异步数据加载
await Future.delayed(Duration(seconds: 1));
if (mounted) { // 检查Widget是否还存在
setState(() {
_counter = 10;
});
}
}
// 4. didChangeDependencies - 依赖变化时调用
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('🔄 4. didChangeDependencies() - 依赖改变');
}
// 5. build - 构建UI
@override
Widget build(BuildContext context) {
print('🎨 5. build() - 构建UI, counter: $_counter');
return Scaffold(
appBar: AppBar(
title: Text(_title),
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _refresh,
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'生命周期演示',
style: Theme.of(context).textTheme.headlineMedium,
),
SizedBox(height: 20),
Text(
'计数器:$_counter',
style: Theme.of(context).textTheme.headlineSmall,
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _increment,
child: Text('增加'),
),
ElevatedButton(
onPressed: _decrement,
child: Text('减少'),
),
],
),
],
),
),
);
}
// 6. didUpdateWidget - Widget配置改变时调用
@override
void didUpdateWidget(LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('🔃 6. didUpdateWidget() - Widget更新');
if (oldWidget.title != widget.title) {
setState(() {
_title = widget.title;
});
}
}
// 7. setState - 状态改变时调用
void _increment() {
setState(() {
print('📈 setState() - 增加计数器');
_counter++;
});
}
void _decrement() {
setState(() {
print('📉 setState() - 减少计数器');
_counter--;
});
}
void _refresh() {
setState(() {
print('🔄 setState() - 刷新状态');
_counter = 0;
});
}
// 8. deactivate - Widget被移除时调用
@override
void deactivate() {
print('⏸️ 8. deactivate() - Widget被停用');
super.deactivate();
}
// 9. dispose - 最终清理
@override
void dispose() {
print('🗑️ 9. dispose() - 清理资源');
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// 应用生命周期监听
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print('📱 应用状态改变:$state');
switch (state) {
case AppLifecycleState.resumed:
print('📱 应用回到前台');
break;
case AppLifecycleState.inactive:
print('📱 应用失去焦点');
break;
case AppLifecycleState.paused:
print('📱 应用暂停');
break;
case AppLifecycleState.detached:
print('📱 应用分离');
break;
}
}
}
3.2.3 State管理最佳实践
// 复杂State管理示例
class ComplexStateWidget extends StatefulWidget {
@override
_ComplexStateWidgetState createState() => _ComplexStateWidgetState();
}
class _ComplexStateWidgetState extends State<ComplexStateWidget> {
// 状态分组管理
late UserController _userController;
late AnimationController _animationController;
late ScrollController _scrollController;
// UI状态
bool _isLoading = false;
String? _errorMessage;
List<User> _users = [];
@override
void initState() {
super.initState();
// 初始化控制器
_userController = UserController();
_animationController = AnimationController(
duration: Duration(milliseconds: 300),
vsync: this,
);
_scrollController = ScrollController();
// 加载初始数据
_loadUsers();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('复杂状态管理')),
body: _buildBody(),
floatingActionButton: _buildFAB(),
);
}
Widget _buildBody() {
if (_isLoading) {
return Center(child: CircularProgressIndicator());
}
if (_errorMessage != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, size: 64, color: Colors.red),
SizedBox(height: 16),
Text(_errorMessage!),
ElevatedButton(
onPressed: _loadUsers,
child: Text('重试'),
),
],
),
);
}
return RefreshIndicator(
onRefresh: _loadUsers,
child: ListView.builder(
controller: _scrollController,
itemCount: _users.length,
itemBuilder: (context, index) {
return UserListItem(
user: _users[index],
onTap: () => _showUserDetails(_users[index]),
onDelete: () => _deleteUser(_users[index]),
);
},
),
);
}
Widget _buildFAB() {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.scale(
scale: 1.0 + (_animationController.value * 0.1),
child: FloatingActionButton(
onPressed: _addUser,
child: Icon(Icons.add),
),
);
},
);
}
// 状态更新方法
Future<void> _loadUsers() async {
if (!mounted) return;
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final users = await _userController.fetchUsers();
if (mounted) {
setState(() {
_users = users;
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_errorMessage = '加载用户失败:$e';
_isLoading = false;
});
}
}
}
void _addUser() {
_animationController.forward().then((_) {
_animationController.reverse();
});
// 显示添加用户对话框
_showAddUserDialog();
}
void _deleteUser(User user) async {
final confirmed = await _showConfirmDialog('确定删除用户 ${user.name}?');
if (confirmed) {
setState(() {
_users.remove(user);
});
}
}
void _showUserDetails(User user) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserDetailPage(user: user),
),
);
}
void _showAddUserDialog() {
showDialog(
context: context,
builder: (context) => AddUserDialog(
onUserAdded: (user) {
setState(() {
_users.add(user);
});
},
),
);
}
Future<bool> _showConfirmDialog(String message) async {
return await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('确认'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text('确定'),
),
],
),
) ?? false;
}
@override
void dispose() {
_userController.dispose();
_animationController.dispose();
_scrollController.dispose();
super.dispose();
}
}
// 用户控制器
class UserController {
Future<List<User>> fetchUsers() async {
// 模拟网络请求
await Future.delayed(Duration(seconds: 2));
return [
User(
name: '张三',
email: 'zhangsan@example.com',
avatarUrl: 'https://via.placeholder.com/100',
bio: 'Flutter开发者',
),
User(
name: '李四',
email: 'lisi@example.com',
avatarUrl: 'https://via.placeholder.com/100',
bio: 'UI设计师',
),
];
}
void dispose() {
// 清理资源
}
}
// 用户列表项组件
class UserListItem extends StatelessWidget {
final User user;
final VoidCallback onTap;
final VoidCallback onDelete;
const UserListItem({
Key? key,
required this.user,
required this.onTap,
required this.onDelete,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dismissible(
key: Key(user.email),
direction: DismissDirection.endToStart,
onDismissed: (direction) => onDelete(),
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.delete, color: Colors.white),
),
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(user.avatarUrl),
),
title: Text(user.name),
subtitle: Text(user.email),
onTap: onTap,
),
);
}
}
3.3 布局Widget详解
3.3.1 Row和Column深入理解
// Row和Column的完整使用示例
class RowColumnDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Row和Column详解')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row布局演示
_buildSectionTitle('Row布局演示'),
_buildRowExamples(),
SizedBox(height: 32),
// Column布局演示
_buildSectionTitle('Column布局演示'),
_buildColumnExamples(),
SizedBox(height: 32),
// 嵌套布局演示
_buildSectionTitle('嵌套布局演示'),
_buildNestedExample(),
],
),
),
);
}
Widget _buildSectionTitle(String title) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 8),
child: Text(
title,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
);
}
Widget _buildRowExamples() {
return Column(
children: [
// 基本Row布局
_buildExample(
'基本Row布局',
Row(
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
),
),
// MainAxisAlignment演示
_buildExample(
'MainAxisAlignment.spaceEvenly',
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildColorBox(Colors.red, '1'),
_buildColorBox(Colors.green, '2'),
_buildColorBox(Colors.blue, '3'),
],
),
),
_buildExample(
'MainAxisAlignment.spaceBetween',
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildColorBox(Colors.red, '1'),
_buildColorBox(Colors.green, '2'),
_buildColorBox(Colors.blue, '3'),
],
),
),
// CrossAxisAlignment演示
_buildExample(
'CrossAxisAlignment.start',
Container(
height: 100,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildColorBox(Colors.red, '1', height: 40),
_buildColorBox(Colors.green, '2', height: 60),
_buildColorBox(Colors.blue, '3', height: 80),
],
),
),
),
// Expanded使用
_buildExample(
'Expanded使用',
Row(
children: [
_buildColorBox(Colors.red, '固定'),
Expanded(
flex: 2,
child: _buildColorBox(Colors.green, 'Flex:2'),
),
Expanded(
flex: 1,
child: _buildColorBox(Colors.blue, 'Flex:1'),
),
],
),
),
// Flexible使用
_buildExample(
'Flexible使用',
Row(
children: [
Flexible(
child: Container(
height: 50,
color: Colors.red,
child: Center(
child: Text('这是一段很长的文本,演示Flexible的效果'),
),
),
),
Container(width: 100, height: 50, color: Colors.green),
],
),
),
],
);
}
Widget _buildColumnExamples() {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一列
Expanded(
child: Column(
children: [
_buildExample(
'MainAxisAlignment.start',
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildColorBox(Colors.red, '1'),
_buildColorBox(Colors.green, '2'),
],
),
),
),
_buildExample(
'MainAxisAlignment.center',
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildColorBox(Colors.red, '1'),
_buildColorBox(Colors.green, '2'),
],
),
),
),
],
),
),
SizedBox(width: 16),
// 第二列
Expanded(
child: Column(
children: [
_buildExample(
'CrossAxisAlignment.stretch',
Container(
height: 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildColorBox(Colors.red, '拉伸'),
_buildColorBox(Colors.green, '拉伸'),
],
),
),
),
_buildExample(
'MainAxisSize.min',
Container(
height: 150,
color: Colors.grey[200],
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildColorBox(Colors.red, 'Min'),
_buildColorBox(Colors.green, 'Size'),
],
),
),
),
],
),
),
],
);
}
Widget _buildNestedExample() {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text('复杂嵌套布局示例', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 16),
// 模拟社交媒体卡片布局
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 用户头像
CircleAvatar(
radius: 25,
backgroundColor: Colors.blue,
child: Text('U', style: TextStyle(color: Colors.white)),
),
SizedBox(width: 12),
// 内容区域
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 用户信息行
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'用户名',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
'2小时前',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
],
),
),
Icon(Icons.more_vert, color: Colors.grey),
],
),
SizedBox(height: 8),
// 内容文本
Text('这是一条社交媒体动态的内容示例...'),
SizedBox(height: 12),
// 操作按钮行
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildActionButton(Icons.thumb_up, '点赞'),
_buildActionButton(Icons.comment, '评论'),
_buildActionButton(Icons.share, '分享'),
],
),
],
),
),
],
),
],
),
);
}
Widget _buildActionButton(IconData icon, String label) {
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 16, color: Colors.grey),
SizedBox(width: 4),
Text(label, style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
);
}
Widget _buildExample(String title, Widget child) {
return Container(
width: double.infinity,
margin: EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 14, color: Colors.grey[600])),
SizedBox(height: 8),
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(4),
),
child: child,
),
],
),
);
}
Widget _buildColorBox(Color color, String text, {double? height}) {
return Container(
width: 60,
height: height ?? 50,
color: color,
child: Center(
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
);
}
}
3.3.2 Stack和Positioned高级用法
// Stack和Positioned完整演示
class StackPositionedDemo extends StatefulWidget {
@override
_StackPositionedDemoState createState() => _StackPositionedDemoState();
}
class _StackPositionedDemoState extends State<StackPositionedDemo>
with TickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
_animationController.repeat(reverse: true);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stack和Positioned详解')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
_buildBasicStackExample(),
SizedBox(height: 32),
_buildPositionedExample(),
SizedBox(height: 32),
_buildAnimatedStackExample(),
SizedBox(height: 32),
_buildComplexLayoutExample(),
],
),
),
);
}
Widget _buildBasicStackExample() {
return _buildExampleCard(
'基本Stack布局',
Container(
height: 200,
child: Stack(
children: [
// 背景容器
Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[300]!, Colors.blue[600]!],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
),
),
// 左上角元素
Positioned(
top: 16,
left: 16,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Text('左上角', style: TextStyle(fontSize: 12)),
),
),
// 右上角元素
Positioned(
top: 16,
right: 16,
child: Icon(Icons.favorite, color: Colors.white),
),
// 居中元素
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, size: 48, color: Colors.white),
Text(
'居中内容',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
// 右下角元素
Positioned(
bottom: 16,
right: 16,
child: FloatingActionButton(
mini: true,
onPressed: () {},
child: Icon(Icons.add),
),
),
],
),
),
);
}
Widget _buildPositionedExample() {
return _buildExampleCard(
'Positioned详细用法',
Container(
height: 250,
child: Stack(
children: [
// 背景网格
CustomPaint(
size: Size(double.infinity, double.infinity),
painter: GridPainter(),
),
// Positioned.fill - 填满整个Stack
Positioned.fill(
child: Container(
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
border: Border.all(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'Positioned.fill',
style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
),
),
),
),
// Positioned.fromRect - 使用矩形定位
Positioned.fromRect(
rect: Rect.fromLTWH(50, 50, 100, 60),
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'fromRect',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
),
// Positioned.directional - 支持RTL
Positioned.directional(
textDirection: TextDirection.ltr,
start: 16,
bottom: 16,
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Directional',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
// 相对定位
Positioned(
top: 100,
right: 20,
width: 80,
height: 40,
child: Container(
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'固定尺寸',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
),
),
],
),
),
);
}
Widget _buildAnimatedStackExample() {
return _buildExampleCard(
'动画Stack效果',
Container(
height: 200,
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Stack(
children: [
// 背景
Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
),
// 动画圆圈1
AnimatedPositioned(
duration: Duration(milliseconds: 100),
left: 20 + (_animation.value * 200),
top: 20,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
// 动画圆圈2
AnimatedPositioned(
duration: Duration(milliseconds: 100),
right: 20 + (_animation.value * 200),
top: 80,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
),
),
// 动画圆圈3
AnimatedPositioned(
duration: Duration(milliseconds: 100),
left: 20 + (_animation.value * 200),
bottom: 20,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
// 中心文本
Center(
child: Text(
'动画演示',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
),
],
);
},
),
),
);
}
Widget _buildComplexLayoutExample() {
return _buildExampleCard(
'复杂Stack布局 - 卡片叠加效果',
Container(
height: 300,
child: Stack(
children: [
// 卡片1 - 最底层
Positioned(
top: 40,
left: 30,
right: 30,
bottom: 80,
child: Transform.rotate(
angle: -0.05,
child: _buildCard(Colors.red, '卡片 1'),
),
),
// 卡片2 - 中层
Positioned(
top: 30,
left: 20,
right: 20,
bottom: 60,
child: Transform.rotate(
angle: 0.02,
child: _buildCard(Colors.green, '卡片 2'),
),
),
// 卡片3 - 顶层
Positioned(
top: 20,
left: 10,
right: 10,
bottom: 40,
child: _buildCard(Colors.blue, '卡片 3'),
),
// 装饰元素
Positioned(
top: 0,
right: 0,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: Icon(Icons.star, color: Colors.white),
),
),
// 底部操作栏
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, -2),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(Icons.shuffle),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
),
),
),
],
),
),
);
}
Widget _buildCard(Color color, String title) {
return Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: Center(
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
);
}
Widget _buildExampleCard(String title, Widget child) {
return Card(
elevation: 4,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
SizedBox(height: 16),
child,
],
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
// 网格绘制器
class GridPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.grey[300]!
..strokeWidth = 1;
// 绘制垂直线
for (double x = 0; x <= size.width; x += 20) {
canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint);
}
// 绘制水平线
for (double y = 0; y <= size.height; y += 20) {
canvas.drawLine(Offset(0, y), Offset(size.width, y), paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
3.4 容器Widget深入理解
3.4.1 Container全面解析
// Container完整功能演示
class ContainerDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Container详解')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('基础Container'),
_buildBasicContainerExamples(),
SizedBox(height: 24),
_buildSectionTitle('装饰效果'),
_buildDecorationExamples(),
SizedBox(height: 24),
_buildSectionTitle('变换效果'),
_buildTransformExamples(),
SizedBox(height: 24),
_buildSectionTitle('实际应用案例'),
_buildPracticalExamples(),
],
),
),
);
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue[700],
),
),
);
}
Widget _buildBasicContainerExamples() {
return Column(
children: [
// 基础Container
_buildExample(
'基础Container - 尺寸和颜色',
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 80,
height: 80,
color: Colors.red,
child: Center(child: Text('固定尺寸', style: TextStyle(color: Colors.white))),
),
Container(
padding: EdgeInsets.all(16),
color: Colors.green,
child: Text('内边距', style: TextStyle(color: Colors.white)),
),
Container(
margin: EdgeInsets.all(8),
padding: EdgeInsets.all(8),
color: Colors.blue,
child: Text('边距+内边距', style: TextStyle(color: Colors.white, fontSize: 10)),
),
],
),
),
// 对齐方式
_buildExample(
'Container对齐 - Alignment',
Row(
children: [
Expanded(
child: Container(
height: 100,
color: Colors.grey[200],
alignment: Alignment.topLeft,
child: Container(
width: 40,
height: 40,
color: Colors.red,
child: Center(child: Text('TL', style: TextStyle(color: Colors.white, fontSize: 10))),
),
),
),
SizedBox(width: 8),
Expanded(
child: Container(
height: 100,
color: Colors.grey[200],
alignment: Alignment.center,
child: Container(
width: 40,
height: 40,
color: Colors.green,
child: Center(child: Text('C', style: TextStyle(color: Colors.white))),
),
),
),
SizedBox(width: 8),
Expanded(
child: Container(
height: 100,
color: Colors.grey[200],
alignment: Alignment.bottomRight,
child: Container(
width: 40,
height: 40,
color: Colors.blue,
child: Center(child: Text('BR', style: TextStyle(color: Colors.white, fontSize: 10))),
),
),
),
],
),
),
// 约束演示
_buildExample(
'Container约束 - Constraints',
Column(
children: [
Container(
constraints: BoxConstraints(
minWidth: 150,
minHeight: 50,
maxWidth: 200,
maxHeight: 80,
),
color: Colors.orange,
child: Text('约束容器\n宽度: 150-200\n高度: 50-80'),
),
SizedBox(height: 8),
Container(
constraints: BoxConstraints.expand(height: 60),
color: Colors.purple,
child: Center(
child: Text('扩展宽度,固定高度', style: TextStyle(color: Colors.white)),
),
),
],
),
),
],
);
}
Widget _buildDecorationExamples() {
return Column(
children: [
// 基础装饰
_buildExample(
'BoxDecoration - 边框和圆角',
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('圆角', style: TextStyle(color: Colors.white))),
),
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.red, width: 3),
borderRadius: BorderRadius.circular(40),
),
child: Center(child: Text('圆形边框', style: TextStyle(color: Colors.red))),
),
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.purple, Colors.pink],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('渐变', style: TextStyle(color: Colors.white))),
),
],
),
),
// 阴影效果
_buildExample(
'BoxShadow - 阴影效果',
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Center(child: Text('基础阴影')),
),
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
spreadRadius: 0,
blurRadius: 10,
offset: Offset(0, 0),
),
],
),
child: Center(child: Text('发光效果')),
),
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 3,
offset: Offset(0, 1),
),
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 0,
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: Center(child: Text('多重阴影')),
),
],
),
),
// 复杂装饰
_buildExample(
'复杂装饰组合',
Container(
height: 120,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.deepPurple[400]!,
Colors.deepPurple[600]!,
Colors.deepPurple[800]!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
boxShadow: [
BoxShadow(
color: Colors.deepPurple.withOpacity(0.3),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: Stack(
children: [
// 背景图案
Positioned.fill(
child: CustomPaint(
painter: PatternPainter(),
),
),
// 内容
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.star, size: 32, color: Colors.white),
SizedBox(height: 8),
Text(
'复杂装饰效果',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
),
],
);
}
Widget _buildTransformExamples() {
return Column(
children: [
_buildExample(
'Transform变换效果',
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 80,
height: 80,
transform: Matrix4.rotationZ(0.2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('旋转', style: TextStyle(color: Colors.white))),
),
Container(
width: 80,
height: 80,
transform: Matrix4.skewX(0.2),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('倾斜', style: TextStyle(color: Colors.white))),
),
Container(
width: 80,
height: 80,
transform: Matrix4.identity()..scale(0.8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('缩放', style: TextStyle(color: Colors.white))),
),
],
),
),
],
);
}
Widget _buildPracticalExamples() {
return Column(
children: [
// 卡片样式
_buildExample(
'实用卡片样式',
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Column(
children: [
// 头部区域
Container(
width: double.infinity,
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[400]!, Colors.blue[600]!],
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
children: [
Icon(Icons.credit_card, color: Colors.white),
SizedBox(width: 12),
Expanded(
child: Text(
'信用卡',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
Text(
'**** 1234',
style: TextStyle(color: Colors.white),
),
],
),
),
// 内容区域
Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('余额', style: TextStyle(color: Colors.grey[600])),
Text(
'¥12,345.67',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(20),
),
child: Text(
'正常',
style: TextStyle(
color: Colors.green[700],
fontWeight: FontWeight.bold,
),
),
),
],
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('转账')),
),
),
SizedBox(width: 12),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'充值',
style: TextStyle(color: Colors.white),
),
),
),
),
],
),
],
),
),
],
),
),
),
],
);
}
Widget _buildExample(String title, Widget child) {
return Container(
width: double.infinity,
margin: EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
),
SizedBox(height: 8),
child,
],
),
);
}
}
// 图案绘制器
class PatternPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white.withOpacity(0.1)
..style = PaintingStyle.stroke
..strokeWidth = 1;
// 绘制菱形图案
for (double x = 0; x < size.width; x += 20) {
for (double y = 0; y < size.height; y += 20) {
final path = Path();
path.moveTo(x + 10, y);
path.lineTo(x + 20, y + 10);
path.lineTo(x + 10, y + 20);
path.lineTo(x, y + 10);
path.close();
canvas.drawPath(path, paint);
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
3.5 滚动Widget详解
3.5.1 ListView完整实现
// ListView完整功能演示
class ListViewDemo extends StatefulWidget {
@override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State<ListViewDemo> {
final ScrollController _scrollController = ScrollController();
final List<String> _items = List.generate(50, (index) => '项目 ${index + 1}');
bool _showScrollButton = false;
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
void _scrollListener() {
if (_scrollController.offset > 200 && !_showScrollButton) {
setState(() => _showScrollButton = true);
} else if (_scrollController.offset <= 200 && _showScrollButton) {
setState(() => _showScrollButton = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ListView详解')),
body: Column(
children: [
// 控制面板
Container(
padding: EdgeInsets.all(16),
color: Colors.grey[100],
child: Row(
children: [
ElevatedButton(
onPressed: _scrollToTop,
child: Text('回到顶部'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _scrollToBottom,
child: Text('滚动到底部'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _addItem,
child: Text('添加项目'),
),
],
),
),
// ListView区域
Expanded(
child: Stack(
children: [
_buildListView(),
// 滚动到顶部按钮
if (_showScrollButton)
Positioned(
right: 16,
bottom: 16,
child: FloatingActionButton(
mini: true,
onPressed: _scrollToTop,
child: Icon(Icons.keyboard_arrow_up),
),
),
],
),
),
],
),
);
}
Widget _buildListView() {
return ListView.builder(
controller: _scrollController,
// 性能优化参数
itemExtent: 80, // 固定项目高度以提升性能
cacheExtent: 500, // 缓存区域大小
itemCount: _items.length + 2, // +2 用于头部和尾部
itemBuilder: (context, index) {
// 头部项目
if (index == 0) {
return _buildHeader();
}
// 尾部项目
if (index == _items.length + 1) {
return _buildFooter();
}
// 普通项目
final itemIndex = index - 1;
return _buildListItem(itemIndex);
},
);
}
Widget _buildHeader() {
return Container(
height: 80,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[400]!, Colors.blue[600]!],
),
),
child: Center(
child: Text(
'ListView 演示头部',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
);
}
Widget _buildFooter() {
return Container(
height: 80,
color: Colors.grey[200],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.done_all, color: Colors.grey[600]),
SizedBox(height: 4),
Text(
'已加载 ${_items.length} 个项目',
style: TextStyle(color: Colors.grey[600]),
),
],
),
),
);
}
Widget _buildListItem(int index) {
return Dismissible(
key: Key(_items[index]),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
final item = _items[index];
setState(() {
_items.removeAt(index);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$item 已删除'),
action: SnackBarAction(
label: '撤销',
onPressed: () {
setState(() {
_items.insert(index, item);
});
},
),
),
);
},
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.delete, color: Colors.white),
),
child: Container(
height: 80,
child: Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: _getColorForIndex(index),
child: Text('${index + 1}'),
),
title: Text(_items[index]),
subtitle: Text('这是第 ${index + 1} 个项目的描述'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _editItem(index),
),
Icon(Icons.drag_handle, color: Colors.grey),
],
),
onTap: () => _showItemDetails(index),
),
),
),
);
}
Color _getColorForIndex(int index) {
final colors = [
Colors.red,
Colors.green,
Colors.blue,
Colors.orange,
Colors.purple,
];
return colors[index % colors.length];
}
void _scrollToTop() {
_scrollController.animateTo(
0,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
void _scrollToBottom() {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
void _addItem() {
setState(() {
_items.add('新项目 ${_items.length + 1}');
});
// 滚动到新添加的项目
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
void _editItem(int index) {
showDialog(
context: context,
builder: (context) {
String newValue = _items[index];
return AlertDialog(
title: Text('编辑项目'),
content: TextField(
controller: TextEditingController(text: newValue),
onChanged: (value) => newValue = value,
decoration: InputDecoration(
labelText: '项目名称',
border: OutlineInputBorder(),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('取消'),
),
TextButton(
onPressed: () {
setState(() {
_items[index] = newValue;
});
Navigator.pop(context);
},
child: Text('保存'),
),
],
);
},
);
}
void _showItemDetails(int index) {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: 300,
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'项目详情',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
Text('名称: ${_items[index]}'),
Text('索引: $index'),
Text('位置: 第 ${index + 1} 个'),
SizedBox(height: 16),
Text('操作:'),
SizedBox(height: 8),
Row(
children: [
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_editItem(index);
},
child: Text('编辑'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
setState(() {
_items.removeAt(index);
});
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: Text('删除'),
),
],
),
],
),
);
},
);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
3.5.2 GridView和CustomScrollView
// GridView和CustomScrollView演示
class GridViewCustomScrollDemo extends StatefulWidget {
@override
_GridViewCustomScrollDemoState createState() => _GridViewCustomScrollDemoState();
}
class _GridViewCustomScrollDemoState extends State<GridViewCustomScrollDemo>
with TickerProviderStateMixin {
late TabController _tabController;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GridView & CustomScrollView'),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: 'GridView'),
Tab(text: 'CustomScrollView'),
Tab(text: '复杂滚动'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
_buildGridViewDemo(),
_buildCustomScrollViewDemo(),
_buildComplexScrollDemo(),
],
),
);
}
Widget _buildGridViewDemo() {
return Column(
children: [
// 控制面板
Container(
padding: EdgeInsets.all(16),
color: Colors.grey[100],
child: Text(
'GridView演示 - 网格布局',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
child: GridView.builder(
padding: EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 每行2个
crossAxisSpacing: 16, // 水平间距
mainAxisSpacing: 16, // 垂直间距
childAspectRatio: 0.8, // 宽高比
),
itemCount: 20,
itemBuilder: (context, index) {
return _buildGridItem(index);
},
),
),
],
);
}
Widget _buildGridItem(int index) {
final colors = [
Colors.red,
Colors.green,
Colors.blue,
Colors.orange,
Colors.purple,
Colors.teal,
];
return Container(
decoration: BoxDecoration(
color: colors[index % colors.length],
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_getIconForIndex(index),
size: 48,
color: Colors.white,
),
SizedBox(height: 8),
Text(
'项目 ${index + 1}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
SizedBox(height: 4),
Text(
'详细描述',
style: TextStyle(
color: Colors.white70,
fontSize: 12,
),
),
],
),
);
}
IconData _getIconForIndex(int index) {
final icons = [
Icons.home,
Icons.star,
Icons.favorite,
Icons.settings,
Icons.person,
Icons.camera,
];
return icons[index % icons.length];
}
Widget _buildCustomScrollViewDemo() {
return CustomScrollView(
slivers: [
// SliverAppBar
SliverAppBar(
expandedHeight: 200,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('可折叠标题'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[400]!, Colors.blue[600]!],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Center(
child: Icon(Icons.landscape, size: 80, color: Colors.white),
),
),
),
),
// SliverPersistentHeader
SliverPersistentHeader(
pinned: true,
delegate: CustomSliverDelegate(
minHeight: 60,
maxHeight: 60,
child: Container(
color: Colors.grey[200],
child: Center(
child: Text(
'固定头部',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
),
),
// SliverList
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
height: 80,
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2,
offset: Offset(0, 1),
),
],
),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('${index + 1}'),
),
title: Text('列表项目 ${index + 1}'),
subtitle: Text('这是第 ${index + 1} 个项目'),
trailing: Icon(Icons.arrow_forward_ios),
),
);
},
childCount: 10,
),
),
// SliverGrid
SliverPadding(
padding: EdgeInsets.all(16),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.orange[300],
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'${index + 1}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
);
},
childCount: 12,
),
),
),
// SliverToBoxAdapter
SliverToBoxAdapter(
child: Container(
height: 100,
margin: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'单个Widget适配器',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
);
}
Widget _buildComplexScrollDemo() {
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
title: Text('嵌套滚动'),
floating: true,
snap: true,
forceElevated: innerBoxIsScrolled,
),
];
},
body: TabBarView(
children: [
// 第一个标签页
ListView.builder(
itemCount: 30,
itemBuilder: (context, index) {
return ListTile(
title: Text('嵌套列表项目 ${index + 1}'),
leading: Icon(Icons.list),
);
},
),
// 第二个标签页
GridView.builder(
padding: EdgeInsets.all(8),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: 20,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('网格 ${index + 1}')),
);
},
),
],
),
);
}
@override
void dispose() {
_tabController.dispose();
_scrollController.dispose();
super.dispose();
}
}
// 自定义Sliver委托
class CustomSliverDelegate extends SliverPersistentHeaderDelegate {
final double minHeight;
final double maxHeight;
final Widget child;
CustomSliverDelegate({
required this.minHeight,
required this.maxHeight,
required this.child,
});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}
@override
double get maxExtent => maxHeight;
@override
double get minExtent => minHeight;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return maxHeight != oldDelegate.maxExtent ||
minHeight != oldDelegate.minExtent;
}
}