flutter:DefaultTabController

  1. 一个案例
  2. DefaultTabController是什么
  3. DefaultTabController内部如何创建TabController
  4. 如果不用 DefaultTabController

一个案例

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'KeepAlive Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const HomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    // DefaultTabController: 提供了一个 Tab 管理器,负责记录当前选中的 Tab
    // length: 2 表示总共有两个 Tab。
    return DefaultTabController( 
      length: 2, // 两个标签页
      child: Scaffold(
        appBar: AppBar(
          title: const Text("AutomaticKeepAliveClientMixin 示例"),
          // bottom: TabBar(...) 表示在底部嵌一个 TabBar(标签页按钮)。
          bottom: const TabBar(
            tabs: [
              Tab(text: "没有 KeepAlive"),
              Tab(text: "有 KeepAlive"),
            ],
          ),
        ),
        // TabBarView 是 Tab 对应的内容区域。
        body: const TabBarView(children: [NoKeepAlivePage(), KeepAlivePage()]),
      ),
    );
  }
}


class NoKeepAlivePage extends StatefulWidget {
  const NoKeepAlivePage({super.key});

  @override
  State<NoKeepAlivePage> createState() => _NoKeepAlivePageState();
}

class _NoKeepAlivePageState extends State<NoKeepAlivePage> {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Counter: $counter", style: const TextStyle(fontSize: 24)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => counter++),
        child: const Icon(Icons.add),
      ),
    );
  }
}


class KeepAlivePage extends StatefulWidget {
  const KeepAlivePage({super.key});

  @override
  State<KeepAlivePage> createState() => _KeepAlivePageState();
}

class _KeepAlivePageState extends State<KeepAlivePage>
    with AutomaticKeepAliveClientMixin {
  int counter = 0;

  @override
  bool get wantKeepAlive => true; 

  @override
  Widget build(BuildContext context) {
    super.build(context); 
    return Scaffold(
      body: Center(
        child: Text("Counter: $counter", style: const TextStyle(fontSize: 24)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => counter++),
        child: const Icon(Icons.add),
      ),
    );
  }
}

DefaultTabController是什么

它是 Flutter 提供的一个封装类,用来管理 TabBar(标签栏)TabBarView(标签页内容) 之间的联动。
它内部创建了一个 TabController,并通过 InheritedWidget 向子树提供。

也就是说:

  • 你不用自己手动写 TabController controller = TabController(length: x, vsync: this);
  • DefaultTabController 自动帮你完成。
  • 子组件(TabBarTabBarView)会通过 DefaultTabController.of(context) 拿到同一个 TabController,从而实现同步。

常用构造函数:

DefaultTabController({
  Key? key,
  required int length,   // 必须:tab 页数量
  int initialIndex = 0,  // 初始显示第几个 tab
  required Widget child, // 子树,通常包含 TabBar + TabBarView
})

DefaultTabController内部如何创建TabController

class DefaultTabController extends StatefulWidget {
  // 构造函数阶段:只是接收参数(length、initialIndex、child)。
  // 里面并没有 TabController。
  const DefaultTabController({
    super.key,
    required this.length,
    this.initialIndex = 0,
    required this.child,
    this.animationDuration,
  }) : assert(length >= 0),
       assert(length == 0 || (initialIndex >= 0 && initialIndex < length));

  final int length;
  final int initialIndex;
  final Widget child;
  final Duration? animationDuration;

  static TabController? of(BuildContext context) {
    final _DefaultTabControllerScope? scope =
      context.dependOnInheritedWidgetOfExactType<_DefaultTabControllerScope>();
    return scope?._controller;
  }

  @override
  _DefaultTabControllerState createState() => _DefaultTabControllerState();
}

class _DefaultTabControllerState extends State<DefaultTabController> {
  TabController? _controller;
	// State 初始化时(initState):
  // 内部真正 new 了一个 TabController,并保存到 _controller。
  @override
  void initState() {
    super.initState();
    // 在初始化方法的时候创建了 TabController,赋值给局部变量
    _controller = TabController(
      length: widget.length,
      vsync: this,   // with TickerProviderStateMixin
      initialIndex: widget.initialIndex,
      animationDuration: widget.animationDuration,
    );
  }

  @override
  Widget build(BuildContext context) {
    //build 阶段:用 _DefaultTabControllerScope 把 _controller 往子树传。
    return _DefaultTabControllerScope(
      controller: _controller!,
      child: widget.child, // 这个是子树,里面有 TabBar 和 TabBarView
    );
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }
}

class _DefaultTabControllerScope extends InheritedWidget {
  const _DefaultTabControllerScope({
    required TabController controller,
    required Widget child,
  }) : _controller = controller, super(child: child);

  final TabController _controller;

  @override
  bool updateShouldNotify(_DefaultTabControllerScope old) {
    return _controller != old._controller;
  }
}

TabBar 在构建时会用 DefaultTabController.of(context) 找到最近的 TabController

TabBarView 内部用的是一个 PageView(本质是个横向滚动的页面容器)。它在构建时也会获取同一个 TabController

然后这两个都会去监听TabController

如果不用 DefaultTabController

class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
  late TabController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TabController(length: 2, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(controller: _controller, tabs: [...]),
      ),
      body: TabBarView(controller: _controller, children: [...]),
    );
  }
}

×

喜欢就点赞,疼爱就打赏