前言

之前弄了不少flutter的内容,这儿直接小小实战一下,就编写微信的 tabbar 上面的前四个页面来锻炼一下咱们的实战能力

—- 仿微信源码地址

别的看看效果(没细搞):

1649747576667.png

MaterialApp

跟其他大多数运用相同,咱们的开发都是从main函数开端的,这儿咱们采用 MaterialApp风格作为咱们根本组件(至少目前没用过其他组件),这儿面包含了咱们的 Router导航等

void main() {
  runApp(const App());
}
//由于没有涉及到状况的变更,一次运用 StatelessWidget 即可
class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    //MaterialApp代表咱们主题纸墨风格框架,咱们一般开发都用这个
    return MaterialApp(
      //去掉debug自选
      debugShowCheckedModeBanner: false,
      //Android任务管理器界面title,能够设置称自己的app姓名
      title: "Flutter Demo",
      //设置我么你的色彩等
      theme: ThemeData(
        scaffoldBackgroundColor: Colors.white,
        brightness: Brightness.light,
        //设置一些card组件的布景色彩,例如一些弹窗组件的布景色彩
        cardColor: const Color.fromRGBO(0x33, 0x33, 0x33, 0.8),
      ),
      //这儿就是咱们的默许主页了
      home: const HomePage()
    );
  }
}

Tabbar的HomePage

HomePage 是咱们编写的 Tabbar的一个界面,其操控谈天(chat)、联系人(contact)、发现(discover)、我的(mine),其他几个页面默许运用空组件占位即可,等待后续编写

别的,每开端一个新界面都需求运用Scaffold组件进行包含,其包含着 appbarbottombar的设置,以便于咱们后续开发(当然单纯封装的UI小组件不需求)

别的 Tabar组件,咱们运用体系的 BottomNavigationBar 还远远不够,咱们运用了PageView持有了其他四个页面的变量,而(在后边讲解为什么不是像ios相同放了4个UI组件在一个数组)

然后经过 PageController操控实践显现界面,此刻 BottomNavigationBar单纯设置 bar 的UI效果,PageViewPageController联动,则负责跳转功能

//编写时咱们承继自StatefulWidget,因为内部会更新状况
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);
  @override
  State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
  //初始化pagecontroller,用来操控page页面
  final _controller = PageController(
    initialPage: 0
  );
  //声明到这儿,能够默许保存,避免内次切换都要从头初始化,假如想从头初始化,能够特别处理
  int _pageIndex = 0;
  @override
  Widget build(BuildContext context) {
    //每开端一个新界面都需求运用Scaffold进行包含,其包含着 appbar和bottombar的设置
    return Scaffold(
      //这是一个 Widget 类型,能够自定义 tabbar,这儿运用体系的来测试
      bottomNavigationBar: BottomNavigationBar(
        //设置当时的index
        currentIndex: _pageIndex,
        //点击后能够用来 index 的选中效果,经过更新页面跳转
        onTap: (int index) {
          setState(() {
            _pageIndex = index;
          });
          //除了更新bottombar图标,还要更新显现哪个页面
          _controller.jumpToPage(index);
        },
        //设置了默许会显现字体,否则只有选中的才显现字体
        unselectedItemColor: Colors.black,
        selectedItemColor: Colors.green,
        //设置为混合类型,默许未选中只显现一个icon图片
        type: BottomNavigationBarType.fixed,
        selectedFontSize: 12,
        unselectedFontSize: 12,
        //设置里面组件
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(
              label: "谈天",
              icon: Image.asset('images/tabbar_chat.png',
                  width: 20,
                  height: 20
              ),
              activeIcon: Image.asset('images/tabbar_chat_hl.png',
                  width: 20,
                  height: 20
              )
          ),
          BottomNavigationBarItem(
              label: "联系人",
              icon: Image.asset('images/tabbar_contact.png',
                  width: 20,
                  height: 20
              ),
              activeIcon: Image.asset('images/tabbar_contact_hl.png',
                  width: 20,
                  height: 20
              )
          ),
          BottomNavigationBarItem(
              label: "发现",
              icon: Image.asset('images/tabbar_discover.png',
                  width: 20,
                  height: 20
              ),
              activeIcon: Image.asset('images/tabbar_discover_hl.png',
                  width: 20,
                  height: 20
              )
          ),
          BottomNavigationBarItem(
            label: "我的",
              icon: Image.asset('images/tabbar_mine.png',
                  width: 20,
                  height: 20
              ),
              activeIcon: Image.asset('images/tabbar_mine_hl.png',
                  width: 20,
                  height: 20
              )
          )
        ],
      ),
      body: PageView(
        controller: _controller,
        //不设置默许能够左右活动,假如不想左右滑动如下设置,能够依据ios或者android来设置
        // physics: const NeverScrollableScrollPhysics(),
        //界面切换后的回调,主要是针对android左右滑动回调,此刻与bottombar无关了
        onPageChanged: (int index) {
          setState(() {
            _pageIndex = index;
          });
        },
        //设置咱们要切换的页面,放到一个数组
        children: const [
          ChatList(),
          Contact(),
          Discover(),
          Mine()
        ],
      ),
    );
  }
}

tabbar的问题点

过错示范

看了相面估量有疑问,我试了一下,将四个页面放到一个数组中,然后直接依据index切换body的实践页面也能够呀,如下所示,我第一次也这么写的(这儿是过错示范)

//过错示范,但看起来效果也是相同
final List _pageList = [ChatList(), Contact(), Discover(), Mine()];
body: _pageList[_pageIndex]

问题

问题一

咱们声明的组件默许在build中有一个根本组件的声明,这是咱们声明的组件,咱们正常能够经过变量来操控某个界面的显现和躲藏,于此一起也操控着一些组件的创立和销毁

因而,当咱们的组件挂载到 body中时,也就意味着每次都切换组件,那么被切换的组件就会开释,而咱们 _pageList 寄存的仅仅没有状况未烘托的widget小组件,跟咱们直接放一个Text相同,每一次切换会从头创立当时组件和烘托,而且强制开释上一个组件的内容,因而其状况无法保存

问题二

然后可能会尝试,发现运用了 PageView,为什么 initStatebuild也会重走呢,这就涉及到了 flutter 的烘托机制,其默许只会烘托当时一屏的内容,且不会保存组件的默许状况,一旦切换其他页面,需求从头初始状况,但有一点该组件并没有开释,咱们的组件一直在 PageView中持有,因而没有开释

体系给咱们提供了一个 maxinAutomaticKeepAliveClientMixin,咱们能够采用多承继的方法,承继自它,那么这个状况就会被保存,就不需求从头烘托后

例如: chat组件,当咱们多承继(with它) AutomaticKeepAliveClientMixin,需求重写他的 wantKeepAlive 属性,设置为 true,然后在 build 中 调用一下 super.build即可

后边章节都会有代码实例,介绍 AutomaticKeepAliveClientMixin 的运用

pubpec.yaml

这个文件包含了咱们的的 sdk版别号、三方仓库、图片资源名称等

//平常用不到,当迁移文件或者改名,记得改一下
name: flutter_wechat_demo
description: 一个仿微信的实战测试demo
//环境sdk版别,一般默许的即可,假如几台电脑不相同,能够依据状况设置最低版别
environment:
  sdk: ">=2.16.1"
//三方依靠,一般运用 flutter pub get + 三方库 方法导入
//在这儿导入,需求点击右上角 Pub get更新
dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.4
//测试版别依靠
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^1.0.0
//图片资源库地址,图片仓库姓名一般与咱们ios、android仓库同级
//放入图片后,只需求右键复制路径,粘到这儿即可
flutter:
  uses-material-design: true
  assets:
   - images/tabbar_chat.png
   - images/tabbar_chat_hl.png
   - images/tabbar_contact.png