Flutter 自定义组件开发指南

发布于:2025-08-17 ⋅ 阅读:(12) ⋅ 点赞:(0)

Flutter 自定义组件指南

在 Flutter 中,自定义组件是构建独特用户界面的核心方式。以下是创建和使用自定义组件的全面指南:

1. 基本自定义组件

创建自定义组件最简单的方式是组合现有组件:

class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;

  const CustomButton({
    required this.text,
    required this.onPressed,
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
      onPressed: onPressed,
      child: Text(text),
    );
  }
}

2. 有状态的自定义组件

当需要维护内部状态时,使用 StatefulWidget

class CounterButton extends StatefulWidget {
  final String label;

  const CounterButton({required this.label, Key? key}) : super(key: key);

  
  _CounterButtonState createState() => _CounterButtonState();
}

class _CounterButtonState extends State<CounterButton> {
  int _count = 0;

  
  Widget build(BuildContext context) {
    return OutlinedButton(
      onPressed: () {
        setState(() {
          _count++;
        });
      },
      child: Text('${widget.label}: $_count'),
    );
  }
}

3. 自定义绘制 (CustomPaint)

对于完全自定义的绘制,使用 CustomPaint

class CircleProgress extends StatelessWidget {
  final double progress;
  
  const CircleProgress({required this.progress, Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(100, 100),
      painter: _CircleProgressPainter(progress),
    );
  }
}

class _CircleProgressPainter extends CustomPainter {
  final double progress;
  
  _CircleProgressPainter(this.progress);

  
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 5
      ..style = PaintingStyle.stroke;
      
    final center = Offset(size.width/2, size.height/2);
    final radius = size.width/2 - 5;
    
    // 绘制背景圆
    canvas.drawCircle(center, radius, paint..color = Colors.grey[300]!);
    
    // 绘制进度弧
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radius),
      -0.5 * pi,
      2 * pi * progress,
      false,
      paint..color = Colors.blue,
    );
  }

  
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

4. 组合复杂组件

class ProfileCard extends StatelessWidget {
  final String name;
  final String role;
  final String imageUrl;
  
  const ProfileCard({
    required this.name,
    required this.role,
    required this.imageUrl,
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            CircleAvatar(
              radius: 40,
              backgroundImage: NetworkImage(imageUrl),
            ),
            SizedBox(height: 16),
            Text(
              name,
              style: Theme.of(context).textTheme.headline6,
            ),
            SizedBox(height: 4),
            Text(
              role,
              style: Theme.of(context).textTheme.subtitle1?.copyWith(
                color: Colors.grey,
              ),
            ),
            SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                IconButton(icon: Icon(Icons.message), onPressed: () {}),
                IconButton(icon: Icon(Icons.phone), onPressed: () {}),
                IconButton(icon: Icon(Icons.email), onPressed: () {}),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

5. 自定义布局组件

class WrapWithPadding extends StatelessWidget {
  final Widget child;
  final EdgeInsets padding;
  
  const WrapWithPadding({
    required this.child,
    this.padding = const EdgeInsets.all(16),
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Padding(
      padding: padding,
      child: child,
    );
  }
}

6. 动画自定义组件

class AnimatedToggle extends StatefulWidget {
  final bool value;
  final ValueChanged<bool> onChanged;
  
  const AnimatedToggle({
    required this.value,
    required this.onChanged,
    Key? key,
  }) : super(key: key);

  
  _AnimatedToggleState createState() => _AnimatedToggleState();
}

class _AnimatedToggleState extends State<AnimatedToggle> 
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: 200),
      vsync: this,
    )..value = widget.value ? 1.0 : 0.0;
  }

  
  void didUpdateWidget(AnimatedToggle oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.value != oldWidget.value) {
      widget.value ? _controller.forward() : _controller.reverse();
    }
  }

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        widget.onChanged(!widget.value);
      },
      child: Container(
        width: 60,
        height: 30,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(15),
          color: Colors.grey[300],
        ),
        child: AnimatedBuilder(
          animation: _controller,
          builder: (context, child) {
            return Stack(
              children: [
                Positioned(
                  left: _controller.value * 30,
                  child: Container(
                    width: 30,
                    height: 30,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: Colors.blue,
                    ),
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }

  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

最佳实践

  1. 保持组件单一职责 - 每个组件应该只做一件事
  2. 合理使用参数 - 通过构造函数参数配置组件行为
  3. 考虑主题一致性 - 使用 Theme.of(context) 保持应用风格统一
  4. 文档注释 - 为公共组件添加文档注释
  5. 性能优化 - 对复杂组件使用 const 构造函数和 shouldRepaint

通过组合和自定义组件,你可以创建出完全符合设计需求的 Flutter 应用界面。


网站公告

今日签到

点亮在社区的每一天
去签到