第三课:Widget核心概念剖析 - Flutter界面构建的基石

一、什么是Widget?理解Flutter的构建理念

1.1 Widget的核心思想

在Flutter中,一切皆Widget。这种设计理念让Flutter的UI构建既灵活又强大:

dart

复制

下载

复制代码
// Widget就像乐高积木
// 小积木 → 大积木 → 完整模型
Text('Hello') → Row([Text, Icon]) → Scaffold(整个页面)

1.2 Widget vs Element vs RenderObject

理解这三个核心概念的关系:

dart

复制

下载

复制代码
// 三棵树架构
Widget树 (配置) → Element树 (实例) → RenderObject树 (渲染)
   ↑                      ↑                     ↑
  配置信息               管理生命周期           实际绘制到屏幕

生动比喻

  • Widget = 建筑蓝图(描述如何建造)

  • Element = 建筑工地(管理建造过程)

  • RenderObject = 实际建筑物(最终成品)

二、StatelessWidget深度解析

2.1 什么是StatelessWidget?

不可变Widget,一旦创建就不能改变。适用于静态内容。

dart

复制

下载

复制代码
class GreetingWidget extends StatelessWidget {
  final String name;
  final Color textColor;
  
  // const构造函数 - 性能优化关键
  const GreetingWidget({
    super.key,
    required this.name,
    this.textColor = Colors.black,
  });
  
  @override
  Widget build(BuildContext context) {
    // build方法每次都会被调用
    return Text(
      '你好,$name!',
      style: TextStyle(
        color: textColor,
        fontSize: 20,
        fontWeight: FontWeight.bold,
      ),
    );
  }
}

// 使用示例
void statelessDemo() {
  return const Scaffold(
    body: Center(
      child: GreetingWidget(
        name: '小明',
        textColor: Colors.blue,
      ),
    ),
  );
}

2.2 StatelessWidget的最佳实践

dart

复制

下载

复制代码
// 1. 始终使用const构造函数(如果可能)
const MyWidget();

// 2. 将Widget拆分为小的、可复用的组件
class UserProfile extends StatelessWidget {
  final User user;
  
  const UserProfile({super.key, required this.user});
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 拆分为更小的Widget
        _buildAvatar(),
        _buildUserName(),
        _buildUserStats(),
      ],
    );
  }
  
  Widget _buildAvatar() {
    return CircleAvatar(
      backgroundImage: NetworkImage(user.avatarUrl),
      radius: 40,
    );
  }
  
  Widget _buildUserName() {
    return Text(
      user.name,
      style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
    );
  }
  
  Widget _buildUserStats() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        _buildStatItem('关注', user.followingCount),
        _buildStatItem('粉丝', user.followerCount),
        _buildStatItem('帖子', user.postCount),
      ],
    );
  }
  
  Widget _buildStatItem(String label, int count) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        children: [
          Text(
            '$count',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          Text(
            label,
            style: TextStyle(color: Colors.grey[600]),
          ),
        ],
      ),
    );
  }
}

2.3 何时使用StatelessWidget?

  • 展示静态数据

  • 不需要用户交互

  • 依赖外部传入的数据

  • 纯展示型组件(按钮、图标、文本等)

三、StatefulWidget深度解析

3.1 什么是StatefulWidget?

可变Widget,可以管理自己的状态变化。

dart

复制

下载

复制代码
class CounterWidget extends StatefulWidget {
  final String title;
  
  const CounterWidget({super.key, required this.title});
  
  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  // 状态变量
  int _counter = 0;
  bool _isFavorite = false;
  
  // 状态更新方法
  void _incrementCounter() {
    setState(() {
      // 在这里修改状态
      _counter++;
    });
    // setState会触发build方法重新调用
  }
  
  void _toggleFavorite() {
    setState(() {
      _isFavorite = !_isFavorite;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    // 每次状态变化都会重新构建
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          widget.title, // 访问widget属性
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 20),
        Text(
          '计数: $_counter',
          style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IconButton(
              onPressed: _incrementCounter,
              icon: const Icon(Icons.add),
              iconSize: 40,
            ),
            IconButton(
              onPressed: _toggleFavorite,
              icon: Icon(
                _isFavorite ? Icons.favorite : Icons.favorite_border,
                color: _isFavorite ? Colors.red : Colors.grey,
              ),
              iconSize: 40,
            ),
          ],
        ),
        // 根据状态显示不同内容
        if (_counter > 10)
          const Text(
            '计数器已经很大了!',
            style: TextStyle(color: Colors.orange),
          ),
      ],
    );
  }
}

3.2 StatefulWidget的生命周期

dart

复制

下载

复制代码
class LifecycleDemo extends StatefulWidget {
  const LifecycleDemo({super.key});
  
  @override
  State<LifecycleDemo> createState() => _LifecycleDemoState();
}

class _LifecycleDemoState extends State<LifecycleDemo> 
    with WidgetsBindingObserver {
    
  int _counter = 0;
  String _status = '初始化';
  
  @override
  void initState() {
    super.initState();
    _status = 'initState: Widget创建';
    print(_status);
    
    // 添加应用状态观察者
    WidgetsBinding.instance.addObserver(this);
    
    // 模拟数据初始化
    Future.delayed(const Duration(seconds: 1), () {
      if (mounted) {
        setState(() {
          _counter = 100;
          _status = '数据加载完成';
        });
      }
    });
  }
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies: 依赖变化');
    // InheritedWidget变化时调用
  }
  
  @override
  void didUpdateWidget(LifecycleDemo oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget: Widget更新');
    // 父Widget重建并传入新配置时调用
  }
  
  @override
  void setState(VoidCallback fn) {
    print('setState: 准备更新状态');
    super.setState(fn);
  }
  
  @override
  void deactivate() {
    print('deactivate: Widget从树中移除');
    super.deactivate();
  }
  
  @override
  void dispose() {
    print('dispose: Widget永久销毁');
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
  
  // WidgetsBindingObserver方法
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('应用状态变化: $state');
  }
  
  @override
  void didChangeMetrics() {
    print('屏幕尺寸变化');
  }
  
  @override
  void didChangeAccessibilityFeatures() {
    print('无障碍功能变化');
  }
  
  @override
  Widget build(BuildContext context) {
    print('build: 构建界面');
    return Scaffold(
      appBar: AppBar(title: const Text('生命周期演示')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('状态: $_status', style: const TextStyle(fontSize: 20)),
            const SizedBox(height: 20),
            Text('计数器: $_counter', style: const TextStyle(fontSize: 48)),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  _counter++;
                  _status = '用户点击增加';
                });
              },
              child: const Text('增加'),
            ),
          ],
        ),
      ),
    );
  }
}

3.3 状态管理的几种模式

dart

复制

下载

复制代码
// 模式1:Widget自身管理状态(适合局部状态)
class SelfManagedCounter extends StatefulWidget {
  const SelfManagedCounter({super.key});
  
  @override
  State<SelfManagedCounter> createState() => _SelfManagedCounterState();
}

// 模式2:父Widget管理状态(适合共享状态)
class ParentManagedCounter extends StatelessWidget {
  final int counter;
  final VoidCallback onIncrement;
  
  const ParentManagedCounter({
    super.key,
    required this.counter,
    required this.onIncrement,
  });
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('计数器: $counter'),
        ElevatedButton(
          onPressed: onIncrement,
          child: const Text('增加'),
        ),
      ],
    );
  }
}

// 模式3:混合模式
class ProductCard extends StatefulWidget {
  final Product product;
  final Function(Product) onAddToCart;
  
  const ProductCard({
    super.key,
    required this.product,
    required this.onAddToCart,
  });
  
  @override
  State<ProductCard> createState() => _ProductCardState();
}

class _ProductCardState extends State<ProductCard> {
  // 本地状态:是否被点击
  bool _isTapped = false;
  
  // 本地状态:动画控制器
  late AnimationController _animationController;
  
  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 200),
      vsync: this,
    );
  }
  
  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
  
  void _handleTap() {
    setState(() {
      _isTapped = true;
    });
    
    _animationController.forward().then((_) {
      widget.onAddToCart(widget.product);
      setState(() {
        _isTapped = false;
      });
      _animationController.reset();
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: _isTapped ? Colors.blue[50] : Colors.white,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: _isTapped ? Colors.blue : Colors.grey[300]!,
          ),
          boxShadow: _isTapped 
            ? [BoxShadow(color: Colors.blue.withOpacity(0.2), blurRadius: 10)]
            : [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4)],
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Image.network(widget.product.imageUrl, height: 120),
            const SizedBox(height: 8),
            Text(widget.product.name, style: const TextStyle(fontSize: 16)),
            Text('¥${widget.product.price}', 
                style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
            Icon(
              _isTapped ? Icons.check_circle : Icons.add_shopping_cart,
              color: _isTapped ? Colors.green : Colors.blue,
            ),
          ],
        ),
      ),
    );
  }
}

四、常用基础Widget详解

4.1 文本与图标

dart

复制

下载

复制代码
class TextAndIconDemo extends StatelessWidget {
  const TextAndIconDemo({super.key});
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 1. 基础Text
            const Text(
              '基础文本',
              style: TextStyle(fontSize: 24),
            ),
            
            // 2. 富文本
            const Text.rich(
              TextSpan(
                text: 'Hello ',
                style: TextStyle(color: Colors.black),
                children: [
                  TextSpan(
                    text: 'Flutter',
                    style: TextStyle(
                      color: Colors.blue,
                      fontWeight: FontWeight.bold,
                      fontSize: 30,
                    ),
                  ),
                  TextSpan(
                    text: ' World!',
                    style: TextStyle(
                      color: Colors.green,
                      fontStyle: FontStyle.italic,
                    ),
                  ),
                ],
              ),
            ),
            
            // 3. 最大行数和溢出
            const Text(
              '这是一个非常长的文本,当它超过最大行数时会显示省略号...',
              maxLines: 2,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(fontSize: 16),
            ),
            
            const SizedBox(height: 20),
            
            // 4. 图标
            const Row(
              children: [
                Icon(Icons.star, color: Colors.amber),
                Icon(Icons.favorite, color: Colors.red),
                Icon(Icons.thumb_up, color: Colors.blue),
                Icon(Icons.share, color: Colors.green),
              ],
            ),
            
            // 5. 图标按钮
            IconButton(
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('图标被点击')),
                );
              },
              icon: const Icon(Icons.settings),
              iconSize: 40,
              color: Colors.purple,
            ),
            
            // 6. 自定义字体
            const Text(
              '自定义字体',
              style: TextStyle(
                fontFamily: 'Roboto',
                fontSize: 24,
                fontWeight: FontWeight.w300,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

4.2 容器与装饰

dart

复制

下载

复制代码
class ContainerDemo extends StatelessWidget {
  const ContainerDemo({super.key});
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            children: [
              // 1. 基础Container
              Container(
                width: 200,
                height: 100,
                color: Colors.blue,
                child: const Center(
                  child: Text(
                    '基础容器',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
              
              const SizedBox(height: 20),
              
              // 2. 带装饰的Container
              Container(
                width: 200,
                height: 100,
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(20),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.grey.withOpacity(0.5),
                      spreadRadius: 2,
                      blurRadius: 7,
                      offset: const Offset(0, 3),
                    ),
                  ],
                  gradient: const LinearGradient(
                    colors: [Colors.pink, Colors.purple],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                  border: Border.all(
                    color: Colors.deepPurple,
                    width: 2,
                  ),
                ),
                child: const Center(
                  child: Text(
                    '装饰容器',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ),
              
              const SizedBox(height: 20),
              
              // 3. 圆形头像
              Container(
                width: 100,
                height: 100,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: Colors.grey[300],
                  image: const DecorationImage(
                    image: NetworkImage('https://picsum.photos/200'),
                    fit: BoxFit.cover,
                  ),
                  border: Border.all(
                    color: Colors.blue,
                    width: 3,
                  ),
                ),
              ),
              
              const SizedBox(height: 20),
              
              // 4. 带内边距和内容的Container
              Container(
                padding: const EdgeInsets.all(20),
                margin: const EdgeInsets.symmetric(horizontal: 20),
                decoration: BoxDecoration(
                  color: Colors.orange[50],
                  borderRadius: BorderRadius.circular(15),
                ),
                child: Column(
                  children: [
                    const Text(
                      '卡片标题',
                      style: TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 10),
                    const Text('这是一个带内边距和外边距的容器'),
                    const SizedBox(height: 10),
                    Container(
                      padding: const EdgeInsets.all(10),
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: const Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text('项目1'),
                          Icon(Icons.arrow_forward_ios, size: 16),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

4.3 布局Widget

dart

复制

下载

复制代码
class LayoutDemo extends StatelessWidget {
  const LayoutDemo({super.key});
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('布局演示')),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 1. Column - 垂直排列
              const Text('Column 垂直布局:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Container(
                padding: const EdgeInsets.all(16),
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    _buildColoredBox(Colors.red, 'Item 1'),
                    _buildColoredBox(Colors.green, 'Item 2'),
                    _buildColoredBox(Colors.blue, 'Item 3'),
                  ],
                ),
              ),
              
              // 2. Row - 水平排列
              const Text('Row 水平布局:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Container(
                padding: const EdgeInsets.all(16),
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    _buildColoredBox(Colors.red, 'Left'),
                    _buildColoredBox(Colors.green, 'Center'),
                    _buildColoredBox(Colors.blue, 'Right'),
                  ],
                ),
              ),
              
              // 3. Stack - 层叠布局
              const Text('Stack 层叠布局:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Container(
                height: 200,
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Stack(
                  children: [
                    // 底层
                    Container(
                      color: Colors.blue[100],
                    ),
                    // 中层
                    Positioned(
                      top: 20,
                      left: 20,
                      child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.red.withOpacity(0.7),
                        child: const Center(child: Text('层1')),
                      ),
                    ),
                    // 上层
                    Positioned(
                      top: 50,
                      left: 50,
                      child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.green.withOpacity(0.7),
                        child: const Center(child: Text('层2')),
                      ),
                    ),
                    // 文字层
                    const Positioned(
                      bottom: 10,
                      right: 10,
                      child: Text('Stack示例'),
                    ),
                  ],
                ),
              ),
              
              // 4. Expanded - 弹性布局
              const Text('Expanded 弹性布局:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Container(
                height: 80,
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  children: [
                    Expanded(
                      flex: 1,
                      child: Container(
                        color: Colors.red,
                        child: const Center(child: Text('1份')),
                      ),
                    ),
                    Expanded(
                      flex: 2,
                      child: Container(
                        color: Colors.green,
                        child: const Center(child: Text('2份')),
                      ),
                    ),
                    Expanded(
                      flex: 3,
                      child: Container(
                        color: Colors.blue,
                        child: const Center(child: Text('3份')),
                      ),
                    ),
                  ],
                ),
              ),
              
              // 5. Wrap - 流式布局
              const Text('Wrap 流式布局:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Container(
                padding: const EdgeInsets.all(16),
                margin: const EdgeInsets.only(bottom: 20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Wrap(
                  spacing: 8, // 水平间距
                  runSpacing: 8, // 垂直间距
                  children: [
                    _buildChip('Flutter'),
                    _buildChip('Dart'),
                    _buildChip('Widget'),
                    _buildChip('Stateful'),
                    _buildChip('Stateless'),
                    _buildChip('Material Design'),
                    _buildChip('Cupertino'),
                    _buildChip('Hot Reload'),
                    _buildChip('Cross Platform'),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
  
  Widget _buildColoredBox(Color color, String text) {
    return Container(
      width: 80,
      height: 60,
      color: color,
      child: Center(
        child: Text(
          text,
          style: const TextStyle(color: Colors.white),
        ),
      ),
    );
  }
  
  Widget _buildChip(String label) {
    return Chip(
      label: Text(label),
      avatar: CircleAvatar(
        backgroundColor: Colors.blue[100],
        child: Text(label[0]),
      ),
      backgroundColor: Colors.blue[50],
    );
  }
}

五、Widget性能优化技巧

5.1 const构造函数的使用

dart

复制

下载

复制代码
class PerformanceDemo extends StatelessWidget {
  const PerformanceDemo({super.key});
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // ❌ 错误示例:每次重建都会创建新对象
        Text('Hello'),
        Container(color: Colors.red),
        Icon(Icons.star),
        
        // ✅ 正确示例:使用const
        const Text('Hello'),
        const Container(color: Colors.red),
        const Icon(Icons.star),
        
        // 甚至整个子树都可以是const
        const Column(
          children: [
            Text('标题'),
            SizedBox(height: 10),
            Text('副标题'),
          ],
        ),
      ],
    );
  }
}

5.2 避免不必要的重建

dart

复制

下载

复制代码
class OptimizedWidget extends StatefulWidget {
  const OptimizedWidget({super.key});
  
  @override
  State<OptimizedWidget> createState() => _OptimizedWidgetState();
}

class _OptimizedWidgetState extends State<OptimizedWidget> {
  int _counter = 0;
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // ❌ 错误:每次重建都会创建新的回调函数
        // ElevatedButton(
        //   onPressed: () => setState(() => _counter++),
        //   child: Text('增加: $_counter'),
        // ),
        
        // ✅ 正确:缓存回调函数
        ElevatedButton(
          onPressed: _incrementCounter,
          child: Text('增加: $_counter'),
        ),
        
        // 使用const构造函数的部分
        const SizedBox(height: 20),
        const Text('静态文本,不会变化'),
        const Icon(Icons.info),
        
        // 使用Builder避免不必要的重建
        Builder(
          builder: (context) {
            // 这部分只会在需要时重建
            return Text('动态内容: $_counter');
          },
        ),
      ],
    );
  }
  
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
}

5.3 使用Key优化列表性能

dart

复制

下载

复制代码
class TodoList extends StatefulWidget {
  const TodoList({super.key});
  
  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  final List<TodoItem> _items = [
    TodoItem(id: '1', text: '学习Flutter'),
    TodoItem(id: '2', text: '写博客'),
    TodoItem(id: '3', text: '做练习'),
  ];
  
  void _addItem() {
    setState(() {
      _items.insert(0, TodoItem(
        id: DateTime.now().toString(),
        text: '新任务 ${_items.length + 1}',
      ));
    });
  }
  
  void _removeItem(int index) {
    setState(() {
      _items.removeAt(index);
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: _items.length,
        // 使用Key优化列表项性能
        itemBuilder: (context, index) {
          final item = _items[index];
          return Dismissible(
            // Key确保Flutter能正确识别每个项
            key: ValueKey(item.id),
            onDismissed: (direction) => _removeItem(index),
            background: Container(color: Colors.red),
            child: ListTile(
              leading: CircleAvatar(
                child: Text('${index + 1}'),
              ),
              title: Text(item.text),
              subtitle: Text('ID: ${item.id}'),
              trailing: IconButton(
                icon: const Icon(Icons.delete),
                onPressed: () => _removeItem(index),
              ),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addItem,
        child: const Icon(Icons.add),
      ),
    );
  }
}

class TodoItem {
  final String id;
  final String text;
  
  TodoItem({required this.id, required this.text});
}

六、实战练习:创建一个完整的微博卡片

dart

复制

下载

复制代码
class WeiboCard extends StatefulWidget {
  final WeiboPost post;
  
  const WeiboCard({super.key, required this.post});
  
  @override
  State<WeiboCard> createState() => _WeiboCardState();
}

class _WeiboCardState extends State<WeiboCard> {
  bool _isLiked = false;
  int _likeCount = 0;
  bool _isExpanded = false;
  
  @override
  void initState() {
    super.initState();
    _likeCount = widget.post.likeCount;
    _isLiked = widget.post.isLiked;
  }
  
  void _toggleLike() {
    setState(() {
      if (_isLiked) {
        _likeCount--;
      } else {
        _likeCount++;
      }
      _isLiked = !_isLiked;
    });
  }
  
  void _toggleExpand() {
    setState(() {
      _isExpanded = !_isExpanded;
    });
  }
  
  void _showCommentSheet() {
    showModalBottomSheet(
      context: context,
      builder: (context) => CommentSheet(postId: widget.post.id),
    );
  }
  
  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      elevation: 2,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 用户信息行
            Row(
              children: [
                CircleAvatar(
                  backgroundImage: NetworkImage(widget.post.userAvatar),
                  radius: 20,
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        widget.post.userName,
                        style: const TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 16,
                        ),
                      ),
                      Text(
                        widget.post.postTime,
                        style: TextStyle(
                          color: Colors.grey[600],
                          fontSize: 12,
                        ),
                      ),
                    ],
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.more_vert),
                  onPressed: () {},
                ),
              ],
            ),
            
            const SizedBox(height: 12),
            
            // 微博内容
            GestureDetector(
              onTap: _toggleExpand,
              child: Text(
                widget.post.content,
                maxLines: _isExpanded ? null : 3,
                overflow: TextOverflow.ellipsis,
                style: const TextStyle(fontSize: 15),
              ),
            ),
            
            // 查看更多按钮
            if (widget.post.content.length > 100 && !_isExpanded)
              TextButton(
                onPressed: _toggleExpand,
                child: const Text('展开全文'),
                style: TextButton.styleFrom(
                  padding: EdgeInsets.zero,
                  minimumSize: Size.zero,
                ),
              ),
            
            // 图片展示
            if (widget.post.images.isNotEmpty)
              Padding(
                padding: const EdgeInsets.only(top: 12),
                child: GridView.builder(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3,
                    crossAxisSpacing: 4,
                    mainAxisSpacing: 4,
                  ),
                  itemCount: widget.post.images.length,
                  itemBuilder: (context, index) {
                    return GestureDetector(
                      onTap: () {
                        // 查看大图
                      },
                      child: Image.network(
                        widget.post.images[index],
                        fit: BoxFit.cover,
                      ),
                    );
                  },
                ),
              ),
            
            // 统计信息
            Padding(
              padding: const EdgeInsets.only(top: 12),
              child: Row(
                children: [
                  Text(
                    '转发 ${widget.post.repostCount}',
                    style: TextStyle(color: Colors.grey[600]),
                  ),
                  const SizedBox(width: 16),
                  Text(
                    '评论 ${widget.post.commentCount}',
                    style: TextStyle(color: Colors.grey[600]),
                  ),
                  const SizedBox(width: 16),
                  Text(
                    '点赞 $_likeCount',
                    style: TextStyle(color: Colors.grey[600]),
                  ),
                ],
              ),
            ),
            
            // 操作按钮
            const Divider(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _buildActionButton(
                  icon: Icons.repeat,
                  label: '转发',
                  onTap: () {},
                ),
                _buildActionButton(
                  icon: Icons.comment,
                  label: '评论',
                  onTap: _showCommentSheet,
                ),
                _buildActionButton(
                  icon: _isLiked ? Icons.favorite : Icons.favorite_border,
                  label: '点赞',
                  color: _isLiked ? Colors.red : Colors.grey,
                  onTap: _toggleLike,
                ),
                _buildActionButton(
                  icon: Icons.share,
                  label: '分享',
                  onTap: () {},
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
  
  Widget _buildActionButton({
    required IconData icon,
    required String label,
    required VoidCallback onTap,
    Color? color,
  }) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(20),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        child: Row(
          children: [
            Icon(icon, color: color ?? Colors.grey[600], size: 20),
            const SizedBox(width: 4),
            Text(label, style: TextStyle(color: Colors.grey[600])),
          ],
        ),
      ),
    );
  }
}

// 数据模型
class WeiboPost {
  final String id;
  final String userName;
  final String userAvatar;
  final String postTime;
  final String content;
  final List<String> images;
  final int repostCount;
  final int commentCount;
  final int likeCount;
  final bool isLiked;
  
  WeiboPost({
    required this.id,
    required this.userName,
    required this.userAvatar,
    required this.postTime,
    required this.content,
    required this.images,
    required this.repostCount,
    required this.commentCount,
    required this.likeCount,
    required this.isLiked,
  });
}
相关推荐
遝靑2 小时前
Flutter 状态管理深度解析:从 Provider 到 Riverpod,再到 Bloc(附选型指南)
flutter
晚霞的不甘2 小时前
Flutter + OpenHarmony 插件开发指南:深度集成原生能力,打造高性能鸿蒙扩展
flutter·华为·harmonyos
庄雨山3 小时前
Flutter Provider 状态管理深度解析与开源鸿蒙 ArkUI 状态管理对比
flutter·provider·openharmonyos
song5013 小时前
鸿蒙 Flutter CI/CD 进阶:Jenkins + 鸿蒙打包自动化流程
分布式·python·flutter·3d·ci/cd·分类
微祎_3 小时前
Flutter 2025 测试体系全景:从单元测试到 E2E,构建高可靠、高覆盖率的自动化质量保障网
flutter·单元测试·自动化
笨小孩7873 小时前
Flutter深度解析:从架构原理到实战应用的跨平台开发指南
flutter·架构
晚霞的不甘4 小时前
Flutter + OpenHarmony 自动化测试体系:从单元测试到多端 E2E 的全流程保障
flutter·单元测试
松☆6 小时前
OpenHarmony 特有挑战:如何让 Flutter 应用支持分布式软总线
分布式·flutter
峰兄19830513 小时前
探索傅里叶变换与短时傅里叶分析:从理论到脚本实践
flutter