Flutter學(xué)習(xí)筆記--仿閑魚(yú)底部導(dǎo)航欄帶有中間凸起圖標(biāo)

image

霸圖鎮(zhèn)樓

仿閑魚(yú)底部導(dǎo)航欄帶有中間凸起圖標(biāo)

需求情景: 需要實(shí)現(xiàn)一個(gè)類似閑魚(yú)APP的底部導(dǎo)航欄的實(shí)現(xiàn)

Demo地址 : https://github.com/hanlin19900610/flutter_bottom_navigation_bar

要實(shí)現(xiàn)的效果如圖
image

好的,下面開(kāi)始上代碼了:

一. 在main.dart文件中,定義APP的入口信息
import 'package:flutter/material.dart';import 'pages/MainPage.dart';void main() => runApp(LightLanguageClient());class LightLanguageClient extends StatelessWidget{    @override
  Widget build(BuildContext context) {        return MaterialApp(
      title: 'Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue
      ),
      home: MainPage(),
    );
  }
}
二. 我們需要定義三個(gè)頁(yè)面,功能類似Android的Fragment,分別為HomePage.dart, AssistantPage.dart,MinePage.dart, 這三個(gè)頁(yè)面的代碼很簡(jiǎn)單:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget{    @override
  State<StatefulWidget> createState() {        return _HomePageState();
  }
}class _HomePageState extends State<HomePage>{   @override
  Widget build(BuildContext context) {        return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('這是首頁(yè)'),
        ),
      ),
    );
  }
}

這個(gè)三個(gè)頁(yè)面的代碼都一樣就沒(méi)有都貼出來(lái)

三.現(xiàn)在我們就需要去創(chuàng)建我們的主頁(yè)了,”MainPage.dart”文件
第一步,我們先去實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的底部導(dǎo)航欄
import 'package:flutter/material.dart';import 'HomePage.dart';import 'AssistantPage.dart';import 'MinePage.dart';class MainPage extends StatefulWidget {    @override
  State<StatefulWidget> createState() {        return _MainPage();
  }
}class _MainPage extends State<MainPage> with SingleTickerProviderStateMixin {
  PageController pageController;    int page = 0;    @override
  Widget build(BuildContext context) {        return MaterialApp(
      home: Scaffold(
        body: new PageView(
          children: <Widget>[HomePage(), AssistantPage(), MinePage()],
          controller: pageController,
          onPageChanged: onPageChanged,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁(yè)')),
          BottomNavigationBarItem(icon: Icon(Icons.assessment), title: Text('助手')),
          BottomNavigationBarItem(icon: Icon(Icons.person), title: Text('我的')),
        ],
        onTap: onTap,
        currentIndex: page,
      ),
    ));
  }    @override
  void initState() {        super.initState();
    pageController = PageController(initialPage: this.page);
  }    @override
  void dispose() {    super.dispose();
    pageController.dispose();   
  }        void onTap(int index) {
    pageController.animateToPage(index,
        duration: const Duration(milliseconds: 300), curve: Curves.ease);
  }      void onPageChanged(int page) {
    setState(() {            this.page = page;
    });
  }
}
在MainPage.dart中我們用到了幾個(gè)控件:
1. PageView : 此控件類似Android的ViewPager,把之前創(chuàng)建的3個(gè)頁(yè)面一次添加進(jìn)去,之后需要給PageView設(shè)置一個(gè)控制器-PageController,給PageView設(shè)置一個(gè)onPageChanged頁(yè)面切換監(jiān)聽(tīng)方法,此方法的功能類似與Android中ViewPager中的OnPageChangeListener里的監(jiān)聽(tīng)方法
2. BottomNavigationBar :此控件主要用于配置底部導(dǎo)航欄,詳細(xì)用法請(qǐng)參見(jiàn)官方文檔,在此控件的使用中,我們需要設(shè)置三個(gè)屬性,
  1. items: 添加底部導(dǎo)航欄的每個(gè)Item

  2. onTap: 為底部導(dǎo)航欄設(shè)置點(diǎn)擊事件

  3. currentIndex: 為底部導(dǎo)航設(shè)置當(dāng)前選中項(xiàng)

image

四.我們要實(shí)現(xiàn)仿閑魚(yú)的底部導(dǎo)航欄,需要重構(gòu)一下底部導(dǎo)航欄,

重構(gòu)方案:
1.把中間的文字去掉
2.在BottomNavigationBar控件的中上的位置放入一個(gè)圖片
3.重構(gòu)底部導(dǎo)航的事件方法
4.禁止PageView的滑動(dòng)事件
現(xiàn)在開(kāi)始重構(gòu):
1.要在BottomNavigationBar上面覆蓋一個(gè)圖片,我們需要用到一個(gè)布局Widget—-Stack,類似于Framelayout
class _MainPage extends State<MainPage> with SingleTickerProviderStateMixin {
  PageController pageController;   int page = 0;  //添加圖片地址,需要?jiǎng)討B(tài)更換圖片
  String bigImg = 'images/home_green.png';   @override
  Widget build(BuildContext context) {        return MaterialApp(
      home: Scaffold(
      body: new PageView(
        children: <Widget>[HomePage(), AssistantPage(), MinePage()],
        controller: pageController,
        onPageChanged: onPageChanged,
      ),      //重構(gòu)bottomNavigationBar
      bottomNavigationBar: Stack(
        children: <Widget>[
          Align(
            alignment: Alignment.bottomCenter,
            child: BottomNavigationBar(
              items: [
                BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁(yè)')),
                BottomNavigationBarItem(icon: Icon(Icons.assessment), title: Text('')),
                BottomNavigationBarItem(icon: Icon(Icons.person), title: Text('我的')),
              ],
              onTap: onTap,
              currentIndex: page,
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: InkWell(
              child: new Image.asset(bigImg,width: 80.0,height: 80.0,),
              onTap:onBigImgTap,
            ),
          )
        ],
      )
    ));
  }    @override
  void initState() {        super.initState();
    pageController = PageController(initialPage: this.page);
  }    @override
  void dispose() {    super.dispose();
    pageController.dispose();    
  }  //修改bottomNavigationBar的點(diǎn)擊事件
  void onTap(int index) {        if (index != 1) {
      setState(() {                this.bigImg = 'images/home_green.png';
      });
    }
    pageController.animateToPage(index,
        duration: const Duration(milliseconds: 300), curve: Curves.ease);
  }  //添加圖片的點(diǎn)擊事件
  void onBigImgTap() {
    setState(() {            this.page = 1;            this.bigImg = 'images/icon_home.png';
      onTap(1);
    });
  }    void onPageChanged(int page) {
    setState(() {            this.page = page;
    });
  }
}

重構(gòu)完成之后,效果圖如下,我們發(fā)現(xiàn)這并不是我們想要的,底部導(dǎo)航欄我們是實(shí)現(xiàn)了,但是PageView被遮蓋了

image
PageView被遮蓋的解決辦法,我們給Stack添加一個(gè)可以指定高度的父級(jí)—Container,修改的代碼如下:
bottomNavigationBar: Container(
            height: 100.0,
            child: Stack(
              children: <Widget>[
                Align(
                  alignment: Alignment.bottomCenter,
                  child: BottomNavigationBar(
                    items: [
                      BottomNavigationBarItem(
                          icon: Icon(Icons.home), title: Text('首頁(yè)')),
                      BottomNavigationBarItem(
                          icon: Icon(Icons.accessibility_new), title: Text('')),
                      BottomNavigationBarItem(
                          icon: Icon(Icons.person), title: Text('我的')),
                    ],
                    onTap: onTap,
                    currentIndex: page,
                  ),
                ),
                Align(
                    alignment: Alignment.bottomCenter,
                    child: Padding(
                      padding: const EdgeInsets.only(bottom: 10.0),
                      child: InkWell(
                        child: new Image.asset(
                          bigImg,
                          width: 80.0,
                          height: 80.0,
                        ),
                        onTap: onBigImgTap,
                      ),
                    )),
              ],
            ),
          )
然后我們需要禁止PageView的滑動(dòng),我們只需要在給PageView設(shè)置一個(gè)屬性就好了
physics: NeverScrollableScrollPhysics(),
在運(yùn)行Flutter項(xiàng)目的時(shí)候出現(xiàn)了一個(gè)問(wèn)題,運(yùn)行時(shí)會(huì)出現(xiàn)一段時(shí)間的白屏,解決辦法:

解決方案很簡(jiǎn)單,Android原生的白屏問(wèn)題可以通過(guò)為 Launcher Activity 設(shè)置 windowBackground 解決,而 Flutter 也是基于此辦法,同時(shí)優(yōu)化了 Flutter 初始化階段的白屏問(wèn)題(覆蓋一個(gè)launchView),只用兩步設(shè)置便能解決 Flutter 中白屏問(wèn)題。

在項(xiàng)目的 android/app/src/main/res/mipmap-xhdpi/ 目錄下添加閃屏圖片;

打開(kāi) android/app/src/main/res/drawable/launch_background.xml 文件,這個(gè)文件就是閃屏的背景文件,具體如何設(shè)置可以查閱 Android Drawable,我在 demo 中的設(shè)置如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@android:color/background_dark" />

  <!-- You can insert your own image assets here -->
  <item
    android:bottom="84dp">
    <bitmap
      android:src="@mipmap/launch_image" />
  </item>
</layer-list>

如此一來(lái),我們就完成了,文章開(kāi)始提出的需求了.

Demo地址 : https://github.com/hanlin19900610/flutter_bottom_navigation_bar

剛開(kāi)始接觸Flutter,代碼寫(xiě)的有些混亂,可能有些問(wèn)題考慮不是很深入,有不足之處,還請(qǐng)各位大佬指出

推薦閱讀

1. Flutter中文網(wǎng) https://flutterchina.club/
2. 阿韋大神的Github https://github.com/AweiLoveAndroid/Flutter-learning
3. Flutter學(xué)習(xí)筆記 - 底部導(dǎo)航欄 https://blog.csdn.net/u011045726/article/details/79583423
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容