Flutter Widgets组件详解:从基础到高级
Flutter Widgets组件详解:从基础到高级
一、Widget基础概念
在Flutter中,一切都是Widget。Widget是Flutter应用的基本构建块,它们描述了UI在某个特定时刻的外观。Flutter的Widget树是应用界面的核心结构。
1.1 Widget的分类
Flutter Widget主要分为两类:
Stateless Widget- 无状态组件
class MyStatelessWidget extends StatelessWidget { final String title; const MyStatelessWidget({super.key, required this.title}); @override Widget build(BuildContext context) { return Text(title); } }Stateful Widget- 有状态组件
class MyStatefulWidget extends StatefulWidget { const MyStatefulWidget({super.key}); @override State<MyStatefulWidget> createState() => _MyStatefulWidgetState(); } class _MyStatefulWidgetState extends State<MyStatefulWidget> { int _counter = 0; void _increment() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Column( children: [ Text('Counter: $_counter'), ElevatedButton(onPressed: _increment, child: const Text('Increment')), ], ); } }1.2 Widget树的结构
MaterialApp( // 根Widget home: Scaffold( // 页面骨架 appBar: AppBar( // 顶部导航栏 title: Text('Title'), ), body: Center( // 居中布局 child: Text('Hello World'), ), ), )二、布局Widget详解
2.1 Container - 容器组件
Container是最常用的布局组件,提供了padding、margin、背景色等功能。
Container( width: 200, height: 100, margin: const EdgeInsets.all(16), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(8), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 4, offset: Offset(2, 2), ), ], ), child: const Text( 'Container', style: TextStyle(color: Colors.white), ), )2.2 Row和Column - 线性布局
Row- 水平布局
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: const [ Icon(Icons.home), Text('Home'), Icon(Icons.settings), ], )Column- 垂直布局
Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: const [ Text('Title'), Text('Subtitle'), Text('Description'), ], )2.3 Flex和Expanded - 弹性布局
Row( children: [ Expanded( flex: 2, child: Container(color: Colors.red), ), Expanded( flex: 1, child: Container(color: Colors.blue), ), Expanded( flex: 1, child: Container(color: Colors.green), ), ], )2.4 Stack - 层叠布局
Stack( alignment: Alignment.center, children: [ Container( width: 200, height: 200, color: Colors.blue, ), const Text( 'Centered', style: TextStyle(color: Colors.white, fontSize: 24), ), Positioned( top: 10, right: 10, child: const Icon(Icons.star, color: Colors.yellow), ), ], )2.5 Wrap - 流式布局
Wrap( spacing: 8, runSpacing: 8, children: [ for (int i = 0; i < 10; i++) Chip( label: Text('Tag $i'), backgroundColor: Colors.grey[200], ), ], )三、Material Design Widget
3.1 AppBar - 应用栏
AppBar( title: const Text('My App'), leading: const Icon(Icons.menu), actions: [ IconButton( icon: const Icon(Icons.search), onPressed: () {}, ), IconButton( icon: const Icon(Icons.more_vert), onPressed: () {}, ), ], elevation: 4, backgroundColor: Colors.blue, )3.2 FloatingActionButton - 悬浮按钮
FloatingActionButton( onPressed: () {}, child: const Icon(Icons.add), backgroundColor: Colors.blue, elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), )3.3 Card - 卡片组件
Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( children: const [ Icon(Icons.person, size: 48), SizedBox(height: 12), Text('John Doe'), Text('john@example.com'), ], ), ), )3.4 ListTile - 列表项
ListTile( leading: const CircleAvatar( backgroundImage: NetworkImage('https://example.com/avatar.jpg'), ), title: const Text('John Doe'), subtitle: const Text('Online'), trailing: const Icon(Icons.arrow_forward_ios), onTap: () {}, )3.5 Button组件
// ElevatedButton - 凸起按钮 ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('Submit'), ) // OutlinedButton - 描边按钮 OutlinedButton( onPressed: () {}, style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.blue), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('Cancel'), ) // TextButton - 文字按钮 TextButton( onPressed: () {}, child: const Text('Learn More'), )四、文本和输入Widget
4.1 Text - 文本组件
const Text( 'Hello Flutter', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue, fontStyle: FontStyle.italic, decoration: TextDecoration.underline, letterSpacing: 2, height: 1.5, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, )4.2 RichText - 富文本
RichText( text: TextSpan( style: DefaultTextStyle.of(context).style, children: const [ TextSpan( text: 'Hello ', style: TextStyle(fontWeight: FontWeight.bold), ), TextSpan( text: 'Flutter', style: TextStyle(color: Colors.blue), ), TextSpan( text: ' World!', style: TextStyle(fontStyle: FontStyle.italic), ), ], ), )4.3 TextField - 文本输入框
TextField( decoration: InputDecoration( labelText: 'Username', hintText: 'Enter your username', prefixIcon: const Icon(Icons.person), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), ), keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, onChanged: (value) { // Handle input changes }, )4.4 TextFormField - 带验证的文本输入
TextFormField( decoration: const InputDecoration( labelText: 'Email', hintText: 'Enter your email', ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your email'; } final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegex.hasMatch(value)) { return 'Please enter a valid email'; } return null; }, )五、图像和图标Widget
5.1 Image - 图片组件
// 从网络加载 Image.network( 'https://example.com/image.jpg', width: 200, height: 200, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return const Center( child: CircularProgressIndicator(), ); }, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.error); }, ) // 从本地资产加载 Image.asset( 'assets/images/logo.png', width: 100, height: 100, )5.2 Icon - 图标组件
const Icon( Icons.home, size: 32, color: Colors.blue, ) // 使用自定义图标字体 Icon( IconData(0xe900, fontFamily: 'MyIcons'), size: 32, )5.3 IconButton - 图标按钮
IconButton( icon: const Icon(Icons.favorite), iconSize: 24, color: Colors.red, onPressed: () {}, splashRadius: 20, )六、滚动Widget
6.1 SingleChildScrollView - 单子滚动
SingleChildScrollView( child: Column( children: [ for (int i = 0; i < 50; i++) ListTile( title: Text('Item $i'), ), ], ), )6.2 ListView - 列表视图
// ListView.builder - 按需构建 ListView.builder( itemCount: 100, itemBuilder: (context, index) { return ListTile( title: Text('Item $index'), ); }, ) // ListView.separated - 带分隔线 ListView.separated( itemCount: 100, separatorBuilder: (context, index) => const Divider(), itemBuilder: (context, index) { return ListTile( title: Text('Item $index'), ); }, )6.3 GridView - 网格视图
GridView.count( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, padding: const EdgeInsets.all(16), children: [ for (int i = 0; i < 12; i++) Container( color: Colors.blue[100], child: Center(child: Text('Grid $i')), ), ], ) // GridView.builder GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: 100, itemBuilder: (context, index) { return Container( color: Colors.blue[100], child: Center(child: Text('Grid $index')), ); }, )七、对话框和弹窗Widget
7.1 AlertDialog - 警告对话框
showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Confirm'), content: const Text('Are you sure you want to delete?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), ElevatedButton( onPressed: () { // Handle delete Navigator.pop(context); }, child: const Text('Delete'), ), ], ), )7.2 SimpleDialog - 简单对话框
showDialog( context: context, builder: (context) => SimpleDialog( title: const Text('Select Option'), children: [ SimpleDialogOption( onPressed: () { Navigator.pop(context, 'Option 1'); }, child: const Text('Option 1'), ), SimpleDialogOption( onPressed: () { Navigator.pop(context, 'Option 2'); }, child: const Text('Option 2'), ), ], ), )7.3 SnackBar - 轻量级提示
ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Message sent successfully'), duration: const Duration(seconds: 2), action: SnackBarAction( label: 'Undo', onPressed: () {}, ), ), )八、自定义Widget
8.1 创建自定义StatelessWidget
class CustomButton extends StatelessWidget { final String text; final VoidCallback onPressed; final Color? color; const CustomButton({ super.key, required this.text, required this.onPressed, this.color, }); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: color ?? Theme.of(context).primaryColor, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text( text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), ); } }8.2 创建自定义StatefulWidget
class CounterWidget extends StatefulWidget { const CounterWidget({super.key}); @override State<CounterWidget> createState() => _CounterWidgetState(); } class _CounterWidgetState extends State<CounterWidget> { int _count = 0; void _increment() => setState(() => _count++); void _decrement() => setState(() => _count--); void _reset() => setState(() => _count = 0); @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$_count', style: const TextStyle(fontSize: 48), ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton(onPressed: _decrement, icon: const Icon(Icons.remove)), const SizedBox(width: 16), CustomButton(text: 'Reset', onPressed: _reset), const SizedBox(width: 16), IconButton(onPressed: _increment, icon: const Icon(Icons.add)), ], ), ], ); } }九、Widget生命周期
9.1 StatefulWidget生命周期方法
class LifecycleDemo extends StatefulWidget { const LifecycleDemo({super.key}); @override State<LifecycleDemo> createState() => _LifecycleDemoState(); } class _LifecycleDemoState extends State<LifecycleDemo> { @override void initState() { super.initState(); // 初始化状态,只调用一次 } @override void didChangeDependencies() { super.didChangeDependencies(); // 依赖变化时调用 } @override void didUpdateWidget(covariant LifecycleDemo oldWidget) { super.didUpdateWidget(oldWidget); // Widget更新时调用 } @override Widget build(BuildContext context) { // 构建UI return const Placeholder(); } @override void dispose() { // 清理资源 super.dispose(); } }十、最佳实践
10.1 Widget组合优于继承
// 推荐:组合现有Widget class UserAvatar extends StatelessWidget { final String url; final double size; const UserAvatar({super.key, required this.url, this.size = 48}); @override Widget build(BuildContext context) { return CircleAvatar( radius: size / 2, backgroundImage: NetworkImage(url), ); } }10.2 提取复杂Widget
// 避免build方法过于庞大 Widget _buildUserCard(User user) { return Card( child: ListTile( leading: UserAvatar(url: user.avatarUrl), title: Text(user.name), subtitle: Text(user.email), ), ); }10.3 使用const构造函数
// 对于不变的Widget使用const const Text('Hello World'); // 优化性能10.4 合理使用Key
ListView.builder( itemBuilder: (context, index) { return ListTile( key: UniqueKey(), // 或ValueKey(item.id) title: Text('Item $index'), ); }, )总结
Flutter的Widget系统是其核心优势之一。通过合理组合和使用各种Widget,你可以构建出丰富多样的UI界面。关键要点:
- 理解Widget树结构:掌握Container、Row、Column、Stack等基础布局Widget
- 合理选择Widget类型:根据场景选择Stateless或Stateful Widget
- 善用Material Design组件:AppBar、Card、Button等组件能快速构建美观界面
- 性能优化:使用ListView.builder、const构造函数等优化渲染性能
- 代码组织:将复杂UI提取为独立Widget,保持代码清晰
继续探索更多Widget和最佳实践,你将能够构建出更加精美和高效的Flutter应用。
