Advanced Flutter

This section will introduce some advanced Flutter concepts to help you improve your development skills.


Rendering Principles

Flutter uses the Skia (gradually switching to Impeller since 3.0) rendering engine to draw UI directly, without using native components.

Widget Tree and Element Tree

  • Widget Tree: Configuration information describing the UI (immutable)
  • Element Tree: Instantiated object of Widget (mutable)
  • RenderObject Tree: Responsible for actual rendering

Understanding rendering principles helps optimize UI performance and avoid unnecessary rebuilds.


InheritedWidget - Data Passing

InheritedWidget allows data to be passed down the Widget tree, and child widgets can access ancestor data.

Example: Custom InheritedWidget

// Define data container
class MyData extends InheritedWidget {
  final int value;

  const MyData({
    super.key,
    required this.value,
    required super.child,
  });

  // Convenience method: get the nearest MyData
  static MyData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>()!;
  }

  // Determine whether the child Widget needs to be rebuilt
  @override
  bool updateShouldNotify(MyData oldWidget) {
    return value != oldWidget.value;
  }
}

// Usage
class ParentWidget extends StatelessWidget {
  const ParentWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return MyData(
      value: 42,
      child: const ChildWidget(),
    );
  }
}

class ChildWidget extends StatelessWidget {
  const ChildWidget({super.key});

  @override
  Widget build(BuildContext context) {
    // Get ancestor's MyData
    final myData = MyData.of(context);
    return Text('Value: ${myData.value}');
  }
}

RepaintBoundary - Local Repaint

Using RepaintBoundary can limit the repaint area and improve performance.

Example: Using RepaintBoundary

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Container(
        // Drawing in this area will not affect the outside
        color: Colors.red,
        child: const Text(Independent Area),
      ),
    );
  }
}

Deep Understanding of Keys

Keys help Flutter identify Widgets and distinguish new Widgets from existing Widgets.

ValueKey vs ObjectKey vs UniqueKey

TypeDescription
ValueKeyUse values as keys (such as IDs, numbers, strings)
ObjectKeyUsing object references as keys
UniqueKeyGenerate a unique key on each creation, destroying and rebuilding

CustomPainter - Custom Drawing

Using CustomPainter can achieve fully custom graphics drawing.

Example: Using CustomPainter

class CustomPainterExample extends StatelessWidget {
  const CustomPainterExample({super.key});

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: MyCirclePainter(),
      size: const Size(200, 200),
    );
  }
}

class MyCirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // Draw a circle
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2 - 10;

    canvas.drawCircle(center, radius, paint);

    // Draw a border
    final borderPaint = Paint()
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawCircle(center, radius, borderPaint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

Stream and Future

Example: Stream Usage

// Create a Stream
Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;  // Send value
  }
}

// Use StreamBuilder
class StreamExample extends StatelessWidget {
  const StreamExample({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: countStream(10),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Text(Count: ${snapshot.data});
        } else if (snapshot.hasError) {
          return Text(Error: ${snapshot.error});
        }
        return const CircularProgressIndicator();
      },
    );
  }
}

Summary

This Flutter introductory tutorial covers the following:

  • Flutter installation and environment configuration
  • Widget Basics (StatelessWidget and StatefulWidget)
  • Layout System (Row, Column, Stack)
  • User Input Handling
  • State Management (setState, Provider)
  • Network requests and data storage
  • Navigation and Routing
  • Testing and Release

For deeper learning, follow:

  • BLoC Pattern
  • Riverpod state management
  • GetX routing and state management
  • Flutter performance optimization
  • Flutter Web and Desktop Development
other extensions