Back to Posts

Rare and Powerful Flutter Widgets You Should Know

10 min read

While basic widgets are essential, Flutter offers many powerful but less commonly used widgets that can solve specific problems elegantly. Let's explore some of these hidden gems.

1. Layout Widgets

Flow Widget

class CustomFlow extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Flow(
      delegate: CustomFlowDelegate(),
      children: List.generate(
        5,
        (index) => Container(
          width: 50,
          height: 50,
          color: Colors.blue.withOpacity(0.2 * (index + 1)),
        ),
      ),
    );
  }
}

class CustomFlowDelegate extends FlowDelegate {
  @override
  void paintChildren(FlowPaintingContext context) {
    for (int i = 0; i < context.childCount; i++) {
      context.paintChild(
        i,
        transform: Matrix4.translationValues(
          i * 20.0,
          i * 20.0,
          0.0,
        ),
      );
    }
  }

  @override
  bool shouldRepaint(CustomFlowDelegate oldDelegate) => false;
}

CustomMultiChildLayout Widget

class CustomMultiChildLayoutExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomMultiChildLayout(
      delegate: CustomMultiChildLayoutDelegate(),
      children: [
        LayoutId(
          id: 'header',
          child: Container(
            color: Colors.blue,
            height: 100,
          ),
        ),
        LayoutId(
          id: 'content',
          child: Container(
            color: Colors.green,
            height: 200,
          ),
        ),
      ],
    );
  }
}

2. Animation Widgets

AnimatedBuilder Widget

class CustomAnimatedBuilder extends StatefulWidget {
  @override
  _CustomAnimatedBuilderState createState() => _CustomAnimatedBuilderState();
}

class _CustomAnimatedBuilderState extends State<CustomAnimatedBuilder>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    );
    _controller.repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Transform.rotate(
          angle: _animation.value * 2 * pi,
          child: child,
        );
      },
      child: Container(
        width: 100,
        height: 100,
        color: Colors.blue,
      ),
    );
  }
}

TweenAnimationBuilder Widget

class CustomTweenAnimationBuilder extends StatefulWidget {
  @override
  _CustomTweenAnimationBuilderState createState() =>
      _CustomTweenAnimationBuilderState();
}

class _CustomTweenAnimationBuilderState
    extends State<CustomTweenAnimationBuilder> {
  double _value = 0.0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TweenAnimationBuilder<double>(
          tween: Tween(begin: 0.0, end: _value),
          duration: Duration(seconds: 1),
          builder: (context, value, child) {
            return Container(
              width: 100,
              height: 100,
              color: Colors.blue.withOpacity(value),
            );
          },
        ),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _value = _value == 0.0 ? 1.0 : 0.0;
            });
          },
          child: Text('Toggle'),
        ),
      ],
    );
  }
}

3. Painting Widgets

CustomPaint Widget

class CustomPainterExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: CustomPainter(),
      child: Container(
        width: 200,
        height: 200,
      ),
    );
  }
}

class CustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0;

    final path = Path()
      ..moveTo(0, size.height / 2)
      ..quadraticBezierTo(
        size.width / 2,
        size.height,
        size.width,
        size.height / 2,
      );

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

RepaintBoundary Widget

class RepaintBoundaryExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: CustomPaint(
        painter: CustomPainter(),
        child: Container(
          width: 200,
          height: 200,
        ),
      ),
    );
  }
}

4. Performance Widgets

Sliver Widgets

class SliverExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
          expandedHeight: 200,
          floating: true,
          pinned: true,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('Sliver Example'),
            background: Image.network(
              'https://example.com/image.jpg',
              fit: BoxFit.cover,
            ),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (context, index) {
              return ListTile(
                title: Text('Item $index'),
              );
            },
            childCount: 20,
          ),
        ),
      ],
    );
  }
}

IndexedStack Widget

class IndexedStackExample extends StatefulWidget {
  @override
  _IndexedStackExampleState createState() => _IndexedStackExampleState();
}

class _IndexedStackExampleState extends State<IndexedStackExample> {
  int _index = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        IndexedStack(
          index: _index,
          children: [
            Container(color: Colors.red),
            Container(color: Colors.green),
            Container(color: Colors.blue),
          ],
        ),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _index = (_index + 1) % 3;
            });
          },
          child: Text('Next'),
        ),
      ],
    );
  }
}

5. Best Practices

  1. Use appropriate widgets

    • Choose based on specific needs
    • Consider performance impact
    • Document usage
  2. Optimize performance

    • Use RepaintBoundary
    • Implement proper keys
    • Handle state efficiently
  3. Test thoroughly

    • Test edge cases
    • Monitor performance
    • Handle errors gracefully

By understanding these rare widgets and following best practices, you can create Flutter applications that are:

  • More efficient
  • More flexible
  • More performant
  • More maintainable