四、Flutter基礎之-界面結(jié)構(gòu)及導航相關

上一篇:三、Flutter基礎—ListView入門


  • 這篇主要介紹下應用界面的結(jié)構(gòu),以及導航相關的小部件,參考王皓的教學視頻,這個教程挺好的,一步一步很詳細適合我這種小白。

先來看看最終效果:

一、MaterialApp和Scaffold

MaterialApp和Scaffold是Flutter提供的兩個Widget

  • MaterialApp是一個方便的Widget,它封裝了應用程序?qū)崿F(xiàn)Material Design所需要的一些Widget。
  • Scaffold組件是Material Design布局結(jié)構(gòu)的基本實現(xiàn)。此類提供了用于顯示drawer、snackbar和底部sheet的API。

MaterialApp組件中提供了如下屬性:

const MaterialApp({
    Key key,
    this.navigatorKey,
    this.home,
    this.routes = const <String, WidgetBuilder>{},
    this.initialRoute,
    this.onGenerateRoute,
    this.onUnknownRoute,
    this.navigatorObservers = const <NavigatorObserver>[],
    this.builder,
    this.title = '',
    this.onGenerateTitle,
    this.color,
    this.theme,
    this.locale,
    this.localizationsDelegates,
    this.localeListResolutionCallback,
    this.localeResolutionCallback,
    this.supportedLocales = const <Locale>[Locale('en', 'US')],
    this.debugShowMaterialGrid = false,
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true,
  })

首先,我們在main.dart中引用import 'package:flutter/material.dart'并返回一個MaterialApp類型app,設置title、主題樣式theme,根視圖home等:

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,  // 不展示右上角"DEBUG"橫幅
      title: 'Olive',
      theme: ThemeData(
        primaryColor: Colors.yellow[300],    // 主題顏色
        highlightColor: Color.fromRGBO(255, 255, 255, 0.5),
        splashColor: Colors.white70,
      ),
      home: MyTabController(),    // 根視圖導航控制器
    );
  }
}

其中MyTabController是自定義的一個dart組件,作為App的根視圖控制器??

二、DefaultTabController

新建一個MyTabController.dart文件,返回一個有狀態(tài)控件DefaultTabController

import 'package:flutter/material.dart';
import '../pages/HomePage.dart';
import '../pages/LeftDrawerPage.dart';
import 'BottomBarPage.dart';

class MyTabController extends StatefulWidget {
  @override
  createState() => new MyTabState();
}

class MyTabState extends State<MyTabController>  {
  int _navIndex = 0;

  // 儲存切換bottomNavigationBar時的四個界面
  var _body = [
    TabBarView(
      children: <Widget>[
        HomePage(),
        Icon(Icons.local_florist, size: 128.0, color: Colors.black12,),
      ]
    ),
    Icon(Icons.local_airport, size: 128.0, color: Colors.black12,),
    Icon(Icons.local_activity, size: 128.0, color: Colors.black12,),
    Icon(Icons.local_cafe, size: 128.0, color: Colors.black12,)
  ];

  void navChange(int index) {
    setState(() {
      _navIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        backgroundColor: Colors.grey[100],
        appBar: _myBar(),
        body: _body[_navIndex],
        drawer: LeftDrawer(),
        bottomNavigationBar: BottomBar(navChange),
      ),
    );
  }
}

其中DefaultTabController的child是Scaffold組件。
在該組件中,通過backgroundColor設置界面背景色;_myBar()為自定義的AppBar類型組件,用于配置導航欄相關;drawer為左側(cè)滑抽屜組件,若想從右側(cè)劃出,則使用endDrawer組件。

LeftDrawer()為自定義的Drawer組件,BottomBar()為底部導航欄,在下面都會講到。

  • AppBar

上面的_myBar()是一個AppBar類型組件,該組件有五大部分:

提供了如下屬性:

AppBar({
    Key key,
    this.leading,    // 左上角控件,一般放置一個icon
    this.automaticallyImplyLeading = true,
    this.title,      // 標題
    this.actions,    // 右上角一系列組件
    this.flexibleSpace,    // 導航欄與bottom間的間隙,見上圖
    this.bottom,             // 底部控件,位置見上圖
    this.elevation = 4.0,    // 陰影Z軸
    this.backgroundColor,    // 背景色
    this.brightness,    // 狀態(tài)欄亮度
    this.iconTheme,    // 圖標樣式
    this.textTheme,    // 文字樣式
    this.primary = true,
    this.centerTitle,    // 標題是否居中顯示,默認值根據(jù)不同的操作系統(tǒng),顯示方式不一樣
    this.titleSpacing = NavigationToolbar.kMiddleSpacing,
    this.toolbarOpacity = 1.0,
    this.bottomOpacity = 1.0,
  })

在本示例中,代碼如下:

Widget _myBar () {
    return AppBar(
      title: Text('Olive'),
      elevation: 5.0,   // 陰影
      actions: <Widget>[
        IconButton(
            icon: Icon(Icons.search),
            tooltip: 'Search',
            onPressed: () => debugPrint('Search button is pressed')
        ),
      ],  // 導航欄右側(cè)按鈕
      bottom: TabBar(
        tabs: <Widget>[
          Tab(icon: Icon(Icons.home)),
          Tab(icon: Icon(Icons.local_florist)),
        ],
        indicatorColor: Colors.black54,
        indicatorSize: TabBarIndicatorSize.label,
        unselectedLabelColor: Colors.black38,
      ),  // 導航欄下方的選項卡
    );  
  }

若沒有自定leading左上角控件,且有drawer左側(cè)抽屜,則默認會創(chuàng)建左上角按鈕,且點擊事件為展開左側(cè)抽屜。

若要使用自定義左側(cè)按鈕來打開抽屜,可使用Scaffold.of(context).openDrawer()方法,具體如下:

leading: new Builder(builder: (BuildContext context){
  return IconButton(
    icon: ClipRRect(
     child: Image.asset('images/nav_user.png', fit: BoxFit.contain, width: 28, height: 28),
     borderRadius: BorderRadius.circular(28/2),
    ),
    onPressed: (){
      Scaffold.of(context).openDrawer();
    },
  );
}),
  • TabBar和TabBarView

TabBar組件為橫向標簽頁,一般結(jié)合TabBarView來使用。
TabBar有如下屬性:

const TabBar({
    Key key,
    @required this.tabs,    // 一系列Tab對象,當然也可以是其他的Widget
    this.controller,               // TabController對象
    this.isScrollable = false,     // 是否可滾動
    this.indicatorColor,           // 指示器顏色
    this.indicatorWeight = 2.0,    // 指示器厚度
    this.indicatorPadding = EdgeInsets.zero,    // 底部指示器的內(nèi)間距
    this.indicator,        // 指示器decoration,例如邊框等
    this.indicatorSize,    // 指示器大小計算方式
    this.labelColor,       // 選中Tab文字顏色
    this.labelStyle,       // 選中Tab文字Style
    this.labelPadding,     // 文字內(nèi)間距
    this.unselectedLabelColor,    // 未選中Tab中文字顏色
    this.unselectedLabelStyle,    // 未選中Tab中文字style
  })

本示例中,在上面_myBar()代碼中,給TabBar添加了兩個按鈕:

bottom: TabBar(
   tabs: <Widget>[
     Tab(icon: Icon(Icons.home)),
     Tab(icon: Icon(Icons.local_florist)),
   ],
   indicatorColor: Colors.black54,
   indicatorSize: TabBarIndicatorSize.label,
   unselectedLabelColor: Colors.black38,
), 

DefaultTabController中設置了TabBarView,控制每個選項卡具體展示的頁面,點擊第一個選項展示HomePage界面,第二個選項卡展示一個Icon,其中HomePage為一個List,這里就不具體說了:

TabBarView(
  children: <Widget>[
    HomePage(),
    Icon(Icons.local_florist, size: 128.0, color: Colors.black12,)
  ]
 ),

三、LeftDrawerPage

新建LeftDrawerPage.dart文件,用于側(cè)滑Drawer布局。廢話不多說,直接上代碼:

import 'package:flutter/material.dart';

class LeftDrawer extends StatelessWidget {

  final String avatarUrl = 'https://upload.jianshu.io/users/upload_avatars/2650319/becf3e53-9113-43e5-8241-de68bcf8b15f.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240';
  final String headerBgImgUrl = 'https://images.unsplash.com/photo-1548693316-8ec65a5f2192?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1225&q=80';

  List<Icon> _iconItems = <Icon>[
    Icon(Icons.message, color: Colors.black12, size: 22,),
    Icon(Icons.favorite, color: Colors.black12, size: 22,),
    Icon(Icons.settings, color: Colors.black12, size: 22,),
  ];

  List<String> _titleItems = <String>[
    'Message', 'Favorite', 'Settings'
  ];

  Widget _listItemBuilder(BuildContext context, int index) {
    return new ListTile(
      title: Text(
        _titleItems[index],
        textAlign: TextAlign.right,
      ),
      trailing: _iconItems[index],
      onTap: () => Navigator.pop(context),
    );
  }

  Widget _listHeaderBuilder() {
    return new UserAccountsDrawerHeader(
      accountName: Text(
        'Olive',
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      accountEmail: Text('461485900@qq.com'),
      currentAccountPicture: CircleAvatar(
        backgroundImage: NetworkImage(avatarUrl),
      ),
      decoration: BoxDecoration(
          color: Colors.yellow[400],
          image: DecorationImage(
            image: NetworkImage(headerBgImgUrl),
            fit: BoxFit.cover,
            colorFilter: ColorFilter.mode(
                Colors.yellow[400].withOpacity(0.6),
                BlendMode.hardLight
            ),
          )
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> _list = new List();
    _list.add(_listHeaderBuilder());
    for (int i = 0; i < _titleItems.length; i++) {
      _list.add(_listItemBuilder(context, i));
    }

    return Drawer(
      child: ListView(
        padding: EdgeInsets.zero,
        children: _list,
      ),
    );
  }
}

示例中Drawer的child中其實是個ListView,具體用法在上一篇文章中有記錄。這里要提的是,竟然有UserAccountsDrawerHeader這種組件!真的很方便,不知道是不是我孤落寡聞哈哈~

UserAccountsDrawerHeader 能快速設置用戶頭像、用戶名、Email 等信息,顯示一個符合紙墨設計規(guī)范的 drawer header。

四、BottomNavigationBar底部導航欄

創(chuàng)建BottomBarPage.dart文件,代碼如下:

import 'package:flutter/material.dart';

class BottomBar extends StatefulWidget {

  ValueChanged<int> _didClickNav;
  BottomBar(this._didClickNav);

  @override
  createState() => new BottomBarState(_didClickNav);
}

class BottomBarState extends State<BottomBar> {
  int _currentIndex = 0;
  ValueChanged<int> _didClickNav;

  BottomBarState(this._didClickNav);
  
  void _onTapHandler (int index) {
    setState(() {
      _currentIndex = index;
      _didClickNav(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      type: BottomNavigationBarType.fixed,
      fixedColor: Colors.black,
      currentIndex: _currentIndex,
      onTap: _onTapHandler,
      items: [
        BottomNavigationBarItem(
            icon: Icon(Icons.explore),
            title: Text('Explore')
        ),
        BottomNavigationBarItem(
            icon: Icon(Icons.history),
            title: Text('History')
        ),
        BottomNavigationBarItem(
            icon: Icon(Icons.list),
            title: Text('List')
        ),
        BottomNavigationBarItem(
            icon: Icon(Icons.person),
            title: Text('My')
        ),
      ],
    );
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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