Flutter TabBar教程|如何在标签间进行程序化导航

151 阅读3分钟

手机上常见的导航模式是显示多个标签,里面有不同的页面。

在Flutter中,这可以通过TabBar部件以及TabControllerTabBarView轻松实现。

但是在Flutter中,你如何以编程方式在这样的标签之间进行导航?

Flutter TabBar。按下按钮后的导航

让我们来弄清楚。👍

作为其中的一部分,我们将看到如何。

  • 当一个按钮被按下时,更新所选的标签。
  • 禁用用户在标签栏上的互动,这样我们就可以引导用户按顺序浏览多个标签

Flutter TabBar的设置

让我们首先创建一个StatefulWidget ,其中有一个TabBar 和一个TabController

// Just a standard StatefulWidget
class JobApplicationFlow extends StatefulWidget {
  const JobApplicationFlow({Key? key}) : super(key: key);

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

// This is where the interesting stuff happens
class _JobApplicationFlowState extends State<JobApplicationFlow>
    with SingleTickerProviderStateMixin {
  // We need a TabController to control the selected tab programmatically
  late final _tabController = TabController(length: 3, vsync: this);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Job Application'),
        // Use TabBar to show the three tabs
        bottom: TabBar(
          controller: _tabController,
          tabs: const <Widget>[
            Tab(
              icon: Icon(Icons.radio_button_on, color: Colors.white),
              text: 'Experience',
            ),
            Tab(
              icon: Icon(Icons.check_box, color: Colors.white),
              text: 'Skills',
            ),
            Tab(
              icon: Icon(Icons.send, color: Colors.white),
              text: 'Submit',
            ),
          ],
        ),
      ),
    );
  }
}

然后,让我们添加一个TabBarView ,包含所有的视图(页面)。

Scaffold(
  appBar: AppBar(...),
  body: TabBarView(
    controller: _tabController,
    children: [
      ExperiencePage(
        onNext: () => _tabController.index = 1,
      ),
      SkillsPage(
        onNext: () => _tabController.index = 2,
      ),
      SubmitPage(
        onSubmit: () => showCupertinoDialog(...),
      ),
    ],
  ),
)

在上面的代码中,每个页面都是一个自定义的小部件,有一些内容和一个按钮,触发onNextonSubmit 回调。

我们可以使用每个回调来改变TabController 的索引。反过来,这也会更新当前的视图(页面)。

禁用用户在TabBar上的互动

这很有效,但是我们仍然可以通过点击标签来浏览它们,这并不总是可取的。

如果我们想 "强迫 "用户按顺序浏览页面,这并不理想。

Flutter TabBar:交互式导航

为了解决这个问题,我们可以创建一个ReadOnlyTabBar ,使用IgnorePointer ,忽略与标签的所有互动(正如StackOverflow的这个主题所提议的)。

// https://stackoverflow.com/a/57354375/436422
class ReadOnlyTabBar extends StatelessWidget implements PreferredSizeWidget {
  final TabBar child;

  const ReadOnlyTabBar({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return IgnorePointer(child: child);
  }

  @override
  Size get preferredSize => child.preferredSize;
}

然后,我们可以用这个新的widget来包装TabBar

AppBar(
  bottom: ReadOnlyTabBar(child:
    TabBar(...),
  ),
)

我们还应该确保我们不能用交互式的拖动手势在标签之间进行切换。NeverScrollableScrollPhysics 可以帮助解决这个问题。

TabBarView(
  // make sure we can't switch tabs with interactive drag gestures
  physics: const NeverScrollableScrollPhysics(),
  controller: _tabController,
  children: [...],
)

就这样了!我们已经使用TabController,TabBar, 和TabBarView 来创建一个多步骤的用户旅程。

为了获得奖励,我们可以显示一个对话框,当我们按OK键时,将TabController 的索引重置为0。

SubmitPage(
  onSubmit: () => showCupertinoDialog(
    context: context,
    builder: (_) {
      return CupertinoAlertDialog(
        title: const Text('Thank you'),
        content: const Text('Your application was submitted.'),
        actions: [
          CupertinoDialogAction(
            child: const Text('OK'),
            onPressed: () {
              // dismiss dialog
              Navigator.of(context).pop();
              _tabController.index = 0;
            },
          ),
        ],
      );
    },
  )
)

下面是最后的结果,再一次。

Flutter TabBar。按下按钮后的导航

结论

FlutterTabBarTabController 类为我们提供了方便的API,我们可以用它来在标签之间进行导航,无论是交互式还是编程式

这使得它们成为将复杂的输入表单分解成更小的表单的理想选择,用户可以更容易地浏览。

你可以在DartPad上找到这个例子的完整源代码。

编码愉快!