Flutter學習筆記--仿閒魚底部導航欄帶有中間凸起圖標

仿閒魚底部導航欄帶有中間凸起圖標

剛接觸Flutter,須要實現一個相似閒魚APP的底部導航欄的實現
源碼連接:  
github.com/hanlin19900…
要實現的效果如圖:


在初版本的時候, 只是爲了實現按鈕凸起的效果,忽略了界面主體部分和底部導航欄之間的間距, 致使出現底部導航欄與界面主體部分有部分空白, 問題已經修改,效果圖如上, 修改代碼見文章底部

好的,下面開始上代碼了:

一. 在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(),
    );
  }
}
複製代碼
二. 咱們須要定義三個頁面,功能相似Android的Fragment,分別爲HomePage.dart, AssistantPage.dart,MinePage.dart, 這三個頁面的代碼很簡單:
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('這是首頁'),
        ),
      ),
    );
  }
}
複製代碼

這個三個頁面的代碼都同樣就沒有都貼出來android

三.如今咱們就須要去建立咱們的主頁了,"MainPage.dart"文件
第一步,咱們先去實現一個最簡單的底部導航欄
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('首頁')),
          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() {
    pageController.dispose();
    super.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中咱們用到了幾個控件:
1. PageView : 此控件相似Android的ViewPager,把以前建立的3個頁面一次添加進去,以後須要給PageView設置一個控制器-PageController,給PageView設置一個onPageChanged頁面切換監聽方法,此方法的功能相似與Android中ViewPager中的OnPageChangeListener裏的監聽方法
2. BottomNavigationBar :此控件主要用於配置底部導航欄,詳細用法請參見官方文檔,在此控件的使用中,咱們須要設置三個屬性,
  1. items: 添加底部導航欄的每一個Item
  2. onTap: 爲底部導航欄設置點擊事件
  3. currentIndex: 爲底部導航設置當前選中項
3. BottomNavigationBarItem: 此控件是底部導航欄的Item
至此,咱們實現了最基本的底部導航欄的實現

四.咱們要實現仿閒魚的底部導航欄,須要重構一下底部導航欄,

重構方案:
1.把中間的文字去掉
2.在BottomNavigationBar控件的中上的位置放入一個圖片
3.重構底部導航的事件方法
4.禁止PageView的滑動事件
如今開始重構:
1.要在BottomNavigationBar上面覆蓋一個圖片,咱們須要用到一個佈局Widget---Stack,相似於Framelayout
class _MainPage extends State<MainPage> with SingleTickerProviderStateMixin {
  PageController pageController;
  int page = 0;
  //添加圖片地址,須要動態更換圖片
  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,
      ),
      //重構bottomNavigationBar
      bottomNavigationBar: Stack(
        children: <Widget>[
          Align(
            alignment: Alignment.bottomCenter,
            child: BottomNavigationBar(
              items: [
                BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁')),
                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() {
    pageController.dispose();
    super.dispose();
  }

//修改bottomNavigationBar的點擊事件
  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);
  }

//添加圖片的點擊事件
  void onBigImgTap() {
    setState(() {
      this.page = 1;
      this.bigImg = 'images/icon_home.png';
      onTap(1);
    });
  }

  void onPageChanged(int page) {
    setState(() {
      this.page = page;
    });
  }
}
複製代碼

重構完成以後,效果圖以下,咱們發現這並非咱們想要的,底部導航欄咱們是實現了,可是PageView被遮蓋了git

PageView被遮蓋的解決辦法,咱們給Stack添加一個能夠指定高度的父級--Container,修改的代碼以下:
bottomNavigationBar: Container(
            height: 100.0,
            child: Stack(
              children: <Widget>[
                Align(
                  alignment: Alignment.bottomCenter,
                  child: BottomNavigationBar(
                    items: [
                      BottomNavigationBarItem(
                          icon: Icon(Icons.home), title: Text('首頁')),
                      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的滑動,咱們只須要在給PageView設置一個屬性就行了
physics: NeverScrollableScrollPhysics(),
複製代碼
在運行Flutter項目的時候出現了一個問題,運行時會出現一段時間的白屏,解決辦法:

解決方案很簡單,Android原生的白屏問題能夠經過爲 Launcher Activity 設置 windowBackground 解決,而 Flutter 也是基於此辦法,同時優化了 Flutter 初始化階段的白屏問題(覆蓋一個launchView),只用兩步設置便能解決 Flutter 中白屏問題。github

在項目的 android/app/src/main/res/mipmap-xhdpi/ 目錄下添加閃屏圖片;bash

打開 android/app/src/main/res/drawable/launch_background.xml 文件,這個文件就是閃屏的背景文件,具體如何設置能夠查閱 Android Drawable,我在 demo 中的設置以下:app

<?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>
複製代碼

如此一來,咱們就完成了,文章開始提出的需求了.

源碼連接:less

github.com/hanlin19900…ide


剛開始接觸Flutter,代碼寫的有些混亂,可能有些問題考慮不是很深刻,有不足之處,還請各位大佬指出佈局


感謝GitHub上大佬提出的問題, 解決辦法以下
//Stack裏面包裹兩個組件: Scaffold 和 FloatingActionButtonhome: Stack(
  children: <Widget>[
    //Scaffold裏面按照以前的寫法body->PageView,bottomNavigationBar直接使用BottomNavigationBar
    Scaffold(
      body: PageView(
        physics: NeverScrollableScrollPhysics(),
        children: <Widget>[
          new HomePage(),
          new AssistantPage(),
          new MinePage()
        ],
        controller: pageController,
        onPageChanged: onPageChanged,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.home), title: Text('首頁')),
          BottomNavigationBarItem(
              icon: Icon(Icons.assessment), title: Text('發佈')),
          BottomNavigationBarItem(
              icon: Icon(Icons.person), title: Text('個人')),
        ],
        onTap: onTap,
        currentIndex: page,
      ),
    ),
    //在整個界面的上層放一個FloatingActionButton 組件,位置在底部居中
    Align(
      child: Padding(
        padding: const EdgeInsets.only(bottom: 30.0),
        child: FloatingActionButton(
          child: new Image.asset(bigImg),
          onPressed: onBigImgTap,
        ),
      ),
      alignment: Alignment.bottomCenter,
    ),
  ],
)複製代碼


推薦閱讀
1. Flutter中文網 
https://flutterchina.club/
2. 阿韋大神的Github 
https://github.com/AweiLoveAndroid/Flutter-learning
3. Flutter學習筆記 - 底部導航欄 
https://blog.csdn.net/u011045726/article/details/79583423
相關文章
相關標籤/搜索