Flutter速来系列23-4、TabBarView,嵌套,圆角指示器,结合Pageview以及最佳实践

Flutter 中的 TabBarView 深度解析

在许多应用程序中,我们经常会看到顶部有多个标签的布局。每个标签在被选中时,下方会显示与之对应的内容。这种布局在 UI 设计中被广泛应用,因为它可以在有限的空间内提供大量的信息。而在 Flutter 中,我们可以通过使用 TabBarTabBarView 来轻松实现这种布局。

一、基本用法

TabBarView的基本概念。

TabBarView是由两个部分组成的:TabBarTabBarView本身。

  • TabBar通常用于显示选项卡,而TabBarView用于显示与选项卡相关联的内容
  • 当用户点击选项卡时,TabBarView将显示与选中选项卡相关联的内容。

文字总是枯燥的,来一个最简单的示例代码吧

import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      home: MyHomePage(),




    );




  }




}









class MyHomePage extends StatelessWidget {


  final List<Tab> myTabs = <Tab>[


    Tab(text: 'Tab 1'),
    Tab(text: 'Tab 2'),
    Tab(text: 'Tab 3'),
  ];



  @override


  Widget build(BuildContext context) {
    return DefaultTabController(
      length: myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: TabBar(
            tabs: myTabs,
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            Center(
              child: Text('Tab 1'),
            ),
            Center(
              child: Text('Tab 2'),
            ),
            Center(

              child: Text('Tab 3'),
            ),

          ],
        ),
      ),
    );
  }
}

image.png

在这个示例中,我们定义了三个选项卡,每个选项卡都有一个标题。然后,在Scaffold部件中,我们定义了一个AppBar,它包含了TabBar。在TabBarView中,我们定义了三个子部件,分别与三个选项卡相关联。

(首先定义了一个 DefaultTabController,这是控制 TabBarTabBarView 交互的必要组件。在 DefaultTabController 中,我们设置了 length 参数为3,这意味着我们将拥有3个标签。)


基本的介绍完了,让我们来进阶一下。


二、进阶:嵌套,动态显示,图文选项卡

嵌套TabBarView

有时,您可能需要在一个TabBarView中嵌套另一个TabBarView。例如,如果您正在开发一个应用程序,其中每个选项卡都需要进一步细分为子选项卡,那么您可能需要使用嵌套的TabBarView。

以下是一个示例:

import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      theme: ThemeData(
        tabBarTheme: TabBarTheme(
          labelColor: Colors.black,
          unselectedLabelColor: Colors.black.withOpacity(0.6),
        ),
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final List<Tab> myTabs = <Tab>[
    Tab(text: 'Tab 1'),
    Tab(text: 'Tab 2'),
    Tab(text: 'Tab 3'),
  ];





  final List<Tab> myNestedTabs = <Tab>[
    Tab(text: 'Nested Tab 1'),
    Tab(text: 'Nested Tab 2'),
    Tab(text: 'Nested Tab 3'),
  ];



  @override


  Widget build(BuildContext context) {


    return DefaultTabController(
      length: myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: TabBar(
            tabs: myTabs,
          ),

        ),
        body: TabBarView(
          children: <Widget>[
            DefaultTabController(
              length: myNestedTabs.length,
              child: Scaffold(
                appBar: AppBar(
                  automaticallyImplyLeading: false,
                  toolbarHeight: 0, // 将内嵌的 AppBar 的高度设置为 0,以隐藏它
                  bottom: TabBar(
                    tabs: myNestedTabs,
                  ),
                ),
                body: TabBarView(
                  children: <Widget>[
                    Center(
                      child: Text('嵌套 Tab 1'),
                    ),
                    Center(
                      child: Text('嵌套 Tab 2'),
                    ),
                    Center(
                      child: Text('嵌套 Tab 3'),
                    ),
                  ],
                ),
              ),
            ),
            Center(
              child: Text('Tab 2'),
            ),
            Center(
              child: Text('Tab 3'),
            ),

          ],
        ),
      ),
    );
  }
}

iShot_2023-07-11_14.54.05.gif

在这个示例中,我们定义了一个嵌套的TabBarView。在第一个选项卡中,我们定义了一个包含嵌套TabBar和TabBarView的Column部件。TabBar用于显示嵌套选项卡,而TabBarView用于显示与选中的嵌套选项卡相关联的内容。注意,在第一个TabBarView中,我们使用了一个Expanded部件,以便内容可以填充整个可用空间。


动态TabBar和TabBarView

有时,您可能需要根据应用程序的状态动态地生成TabBar和TabBarView。例如,如果您正在开发一个电子商务应用程序,其中每个选项卡都需要动态显示商品类别,那么您可能需要动态生成TabBar和TabBarView。以下是一个示例:

import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      home: MyHomePage(),




    );




  }




}









class MyHomePage extends StatefulWidget {

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

class _MyHomePageState extends State<MyHomePage> {
  List<String> categories = ['Category 1', 'Category 2', 'Category 3'];
  List<Widget> tabs = [];

  @override
  void initState() {
    super.initState();
    for (String category in categories) {
      tabs.add(
        Tab(
          text: category,
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: tabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: TabBar(
            tabs: tabs,
          ),
        ),
        body: TabBarView(
          children: categories.map((category) {
            return Center(
              child: Text(category),
            );
          }).toList(),
        ),
      ),
    );
  }
}

在这个示例中,我们定义了一个状态类MyHomePage,并在其中定义了一个categories列表和一个tabs列表。

在initState方法中,我们遍历categories列表,并为每个类别创建一个Tab部件,并将其添加到tabs列表中。

然后,在build方法中,我们使用tabs列表来动态生成TabBar,使用categories列表来动态生成TabBarView

image.png


自定义TabBar和TabBarView

如果标准的TabBar和TabBarView不符合您的需求,您可以使用Flutter的自定义部件来创建自定义TabBar和TabBarView。以下是一个示例:

import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      home: MyHomePage(),




    );




  }




}









class MyHomePage extends StatelessWidget {


  final List<String> categories = ['Category 1', 'Category 2', 'Category 3'];



  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: categories.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: PreferredSize(
            preferredSize: Size.fromHeight(48.0),
            child: Container(
              color: Colors.white,
              child: TabBar(
                isScrollable: true,
                indicatorColor: Colors.red,
                labelColor: Colors.red,
                unselectedLabelColor: Colors.black,
                tabs: categories.map((category) {
                  return Tab(
                    child: Text(
                      category.toUpperCase(),
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                      ),
                    ),
                  );
                }).toList(),
              ),
            ),

          ),
        ),
        body: TabBarView(
          children: categories.map((category) {
            return Center(
              child: Text(category),
            );
          }).toList(),
        ),
      ),
    );
  }
}

image.png

在这个示例中,我们使用了自定义TabBar和TabBarView。在TabBar中,我们设置了一些属性,如isScrollable、indicatorColor和labelColor,以自定义选项卡的外观。在TabBar中,我们还使用了一个自定义的Text部件,以自定义选项卡的文本样式。在TabBarView中,我们使用categories列表来动态生成选项卡的内容。

图片选项卡

除了标准的文本选项卡,Flutter的TabBarView还支持图片选项卡

图文选项卡

要创建图片选项卡,您可以使用Tab部件的icon属性。以下是一个示例:

import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      home: MyHomePage(),




    );




  }




}









class MyHomePage extends StatelessWidget {


  final List<Tab> myTabs = <Tab>[


    Tab(

      icon: Icon(Icons.home),
    ),
    Tab(
      icon: Icon(Icons.search),
    ),
    Tab(
      icon: Icon(Icons.person),
    ),
  ];





  @override


  Widget build(BuildContext context) {
    return DefaultTabController(
      length: myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: TabBar(
            tabs: myTabs,
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            Center(

              child: Text('Home'),
            ),

            Center(
              child: Text('Search'),
            ),

            Center(
              child: Text('Profile'),
            ),

          ],
        ),
      ),
    );
  }
}

在这个示例中,我们定义了三个选项卡,并在每个选项卡中添加了一个图标。要添加图标,我们只需要在Tab部件中使用icon属性,并将其设置为一个Icon部件。在TabBarView中,我们定义了与每个选项卡相关联的内容。

image.png


图片加文字的tab

上面的感觉不够好用

让我们来丰富一下

  • 图标加文本
  • 设置间距
  • 设置文本大小,图标大小
import 'package:flutter/material.dart';















void main() {





  runApp(MyApp());





}











class MyApp extends StatelessWidget {





  @override





  Widget build(BuildContext context) {





    return MaterialApp(





      home: MyHomePage(),




    );




  }




}









class MyHomePage extends StatefulWidget {

  final List<Tab> myTabs = <Tab>[


    Tab(

      child: Column(
        children: [
          Icon(
            Icons.home,
            size: 24, // 调整图标大小
          ),
          SizedBox(width: 8.0), // 调整文本和图标之间的距离
          Text(
            'Home',
            style: TextStyle(fontSize: 16.0), // 调整文本大小
          ),
        ],
      ),
    ),
    Tab(
      child: Column(
        children: [
          Icon(
            Icons.person,
            size: 24, // 调整图标大小
          ),
          SizedBox(width: 8.0), // 调整文本和图标之间的距离
          Text(
            'Profile',
            style: TextStyle(fontSize: 16.0), // 调整文本大小
          ),

        ],
      ),
    ),
  ];

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: widget.myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('TabBarView Example'),
          bottom: TabBar(
            tabs: widget.myTabs,
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            Center(
              child: Text('Home tab content'),
            ),
            Center(
              child: Text('Profile tab content'),
            ),
          ],
        ),
      ),
    );
  }
}

image.png

圆角指示器

import 'package:flutter/material.dart';















void main() => runApp(MyApp());



class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: MyHomePage(),

    );

  }

}



class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}




class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {

  late TabController _tabController;

  final List<String> _tabs = ["Tab 1", "Tab 2", "Tab 3"];



  @override


  void initState() {

    super.initState();

    _tabController = TabController(length: _tabs.length, vsync: this);
  }





  @override


  void dispose() {

    _tabController.dispose();
    super.dispose();

  }




  @override


  Widget build(BuildContext context) {


    return Scaffold(

      appBar: AppBar(

        title: Text('TabBar Example'),
        backgroundColor: Colors.transparent, // TabBar 的背景颜色
        elevation: 0, // 去除阴影
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(48.0),
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(50.0),
              color: Colors.blue, // TabBar 背景颜色
            ),
            child: TabBar(
              controller: _tabController,
              indicator: BoxDecoration(
                borderRadius: BorderRadius.circular(50.0), // 指示器圆角半径
                color: Colors.white60, // 选中 Tab 的背景颜色
              ),
              unselectedLabelColor: Colors.white.withOpacity(0.7),
              labelColor: Colors.blue,
              tabs: _tabs.map((String name) => Tab(text: name)).toList(),
            ),
          ),
        ),
      ),
      body: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(50.0),
            topRight: Radius.circular(50.0),
          ),
          color: Colors.white,
        ),
        child: TabBarView(
          controller: _tabController,
          children: <Widget>[
            Container(
              color: Colors.white,
              child: Center(
                child: Text('Tab 1 Content'),
              ),
            ),

            Container(
              color: Colors.white,
              child: Center(
                child: Text('Tab 2 Content'),
              ),
            ),
            Container(
              color: Colors.white,
              child: Center(
                child: Text('Tab 3 Content'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

image.png

三、TabBarView结合PageView

import 'package:flutter/material.dart';















void main() => runApp(MyApp());



class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: MyHomePage(),

    );

  }

}



class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}




class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {

  late TabController _tabController;

  final List<String> _tabs = ["选项卡一", "选项卡二", "选项卡三"]; // 选项卡的标题



  @override


  void initState() {

    super.initState();

    _tabController = TabController(length: _tabs.length, vsync: this); // 创建 TabController
  }





  @override


  void dispose() {

    _tabController.dispose(); // 释放 TabController
    super.dispose();

  }




  @override


  Widget build(BuildContext context) {


    return Scaffold(

      appBar: AppBar(

        title: Text('TabBarView and PageView 示例'),
      ),
      body: Column(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              color: Colors.white, // TabBar 的背景颜色
              borderRadius: BorderRadius.only(
                topLeft: Radius.circular(20.0),
                topRight: Radius.circular(20.0),
              ), // 设置 TabBar 的边角为圆角
            ),

            child: TabBar(
              controller: _tabController, // 设置 TabBar 的控制器为 _tabController
              indicatorColor: Colors.blue, // 设置指示器的颜色
              labelColor: Colors.blue, // 设置选中的标签的文本颜色
              unselectedLabelColor: Colors.grey, // 设置未选中的标签的文本颜色
              tabs: _tabs.map((String name) => Tab(text: name)).toList(), // 创建选项卡
            ),
          ),
          Expanded(
            child: TabBarView(
              controller: _tabController, // 设置 TabBarView 的控制器为 _tabController
              children: <Widget>[
                PageView(
                  children: <Widget>[
                    Container(
                      color: Colors.red, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡一的页面一'),
                      ),
                    ),
                    Container(
                      color: Colors.green, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡一的页面二'),
                      ),
                    ),
                    Container(
                      color: Colors.blue, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡一的页面三'),
                      ),
                    ),
                  ],
                ),
                PageView(
                  children: <Widget>[
                    Container(
                      color: Colors.pink, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡二的页面一'),
                      ),
                    ),
                    Container(
                      color: Colors.orange, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡二的页面二'),
                      ),
                    ),
                    Container(
                      color: Colors.purple, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡二的页面三'),
                      ),
                    ),
                  ],
                ),
                PageView(
                  children: <Widget>[
                    Container(
                      color: Colors.teal, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡三的页面一'),
                      ),
                    ),
                    Container(
                      color: Colors.yellow, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡三的页面二'),
                      ),
                    ),
                    Container(
                      color: Colors.brown, // 页面的背景颜色
                      child: Center(
                        child: Text('选项卡三的页面三'),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

我们创建了一个带有三个选项卡的 TabBar。每个选项卡中都有一个 PageView,其中包含三个页面。

iShot_2023-07-11_16.10.11.gif

TabBarView的最佳实践和性能优化

懒加载页面

  • 懒加载页面:为了提高性能,您可以使用 PageView.builder 或 ListView.builder 等构建器方法来动态创建页面,以便在需要时才加载页面。这样可以避免同时加载所有页面所带来的性能开销。

设置默认选中的页面、滚动到指定页面等。

  • 使用控制器:使用 TabController 或 PageController 等控制器可以让您更好地控制 TabBarView 和 PageView 的行为,例如设置默认选中的页面、滚动到指定页面等。

避免重复渲染

  • 避免重复渲染:在 TabBarView 和 PageView 中,每次切换选项卡都会触发整个页面的重绘,这可能会导致性能问题。为了避免这种情况,您可以使用 AutomaticKeepAliveClientMixin 来保持页面的状态,并避免重复渲染。

预加载页面

  • 预加载页面:如果您需要在用户切换选项卡时立即显示页面,而不是等待页面加载完成,您可以使用 PageView 的 preloadPagesCount 属性来预加载页面。这会在用户切换选项卡时提前加载页面,以提高用户体验。

  • 优化页面布局:对于每个页面,尽可能使用简单的布局和组件,以避免复杂的布局和组件所带来的性能开销。您可以使用 const 构造函数来创建静态的组件,以避免不必要的重绘。

避免使用过多的页面

  • 避免使用过多的页面:在 TabBarView 中,如果您有太多的选项卡和页面,可能会导致性能问题。为了避免这种情况,您可以使用 ListView 或 GridView 等组件来实现分页效果,以减少页面的数量。

禁用滑动手势

  • 禁用滑动手势:如果您不需要用户通过滑动手势来切换页面,可以将 PageView 的 physics 属性设置为 NeverScrollableScrollPhysics() 来禁用滑动手势。这可以减少用户误操作和提高性能。

这篇,就到这里吧

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYtvoqus' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片