Flutter 1.17 Widget 之 NavigationRail 初探

Flutter程式設計開發發表於2020-05-31

Flutter 1.17  Widget 之 NavigationRail 初探

Flutter 1.17 Widget 之 NavigationRail 初探

Flutter 1.17 版本中加入了一個新的 Widget NavigationRail ,接下來就讓我們來簡單看一下這個 Widget 如何使用。

如果 Flutter SDK 不是最新版本請通過 flutter upgrade 命令來升級 SDK 。

flutter upgrade
複製程式碼

一、NavigationRail 介紹

Flutter 1.17  Widget 之 NavigationRail 初探

NavigationRail 這個元件是一個材料設計風格的元件,通常展示在應用程式的左邊或者右邊,通過這個元件來進行頁面的導航,通常來說頁面數量都是3個或者5個以內。

NavigationRail 一般都是定義為 Row 的第一個或者最後一個元素,而 Row 都是做為 Scaffold 裡面的 body 來進行佈局。

NavigationRail 這個元件是為大屏裝置佈局設計的,如桌面程式、Web 或者平板裝置等大屏裝置上,而像手機這樣的豎屏小屏裝置,一般都是使用 BottomNavigationBar 來實現導航功能。

二、典型的使用方式和頁面結構

和 BottomNavigationBar 一樣具有導航功能,但是 NavigationRail 的使用方式和 BottomNavigationBar 完全不同,不像 BottomNavigationBar 一樣,可以做為Scaffold 中的一個引數來使用 。

一般來說,想要使用 NavigationRail,都是用 Row 做為 Scaffold 的 body 元件,而 NavigationRail 做為 Row 的第一個或者最後一個元素,這樣導航就會定義在最左邊或者最右邊,如果使用了 NavigationRail,那麼剩下的佈局通常都是使用 Expanded,這樣才會佔滿螢幕的剩餘空間。

典型的頁面結構是這樣的。

Flutter 1.17  Widget 之 NavigationRail 初探

當然,完全可以進一步美化,比如,這樣的。

Flutter 1.17  Widget 之 NavigationRail 初探

典型的程式碼結構如下。

Scaffold(
  //row 做為 body 佈局
  body: Row(
    children: <Widget>[
      NavigationRail(
        selectedIndex: _selectedIndex,
        onDestinationSelected: (int index) {
          setState(() {
            _selectedIndex = index;
          });
        },
        labelType: NavigationRailLabelType.selected,
        destinations: [
          NavigationRailDestination(
            icon: Icon(Icons.favorite_border),
            selectedIcon: Icon(Icons.favorite),
            label: Text('First'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.bookmark_border),
            selectedIcon: Icon(Icons.book),
            label: Text('Second'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.star_border),
            selectedIcon: Icon(Icons.star),
            label: Text('Third'),
          ),
        ],
      ),
      VerticalDivider(thickness: 1, width: 1),
      // This is the main content.
      //Expanded 佔滿剩下螢幕空間
      Expanded(
        child: Center(
          child: Text('selectedIndex: $_selectedIndex'),
        ),
      )
    ],
  ),
);
複製程式碼

三、屬性說明

  const NavigationRail({
    this.backgroundColor,           //背景色
    this.extended = false,          //是否是擴充套件狀態
    this.leading,                   //destinations 上的 widget 佈局
    this.trailing,                  // destinations 下方的 widget 
    @required this.destinations,    //導航條
    @required this.selectedIndex,   //當前選中的頁面索引
    this.onDestinationSelected,     //點選了導航的圖示的回撥
    this.elevation,                 //z 軸方向高度
    this.groupAlignment,            //對齊方式
    this.labelType,                 //選擇標籤時的效果
    this.unselectedLabelTextStyle,  //沒有選中的標籤的樣式
    this.selectedLabelTextStyle,    //選中的標籤的樣式
    this.unselectedIconTheme,       //未被選中的標籤文字樣式
    this.selectedIconTheme,         //選中的標籤文字樣式
    this.minWidth,                  
    this.minExtendedWidth,          
  }) :  assert(destinations != null && destinations.length >= 2),
        assert(selectedIndex != null),
        assert(0 <= selectedIndex && selectedIndex < destinations.length),
        assert(elevation == null || elevation > 0),
        assert(minWidth == null || minWidth > 0),
        assert(minExtendedWidth == null || minExtendedWidth > 0),
        assert((minWidth == null || minExtendedWidth == null) || minExtendedWidth >= minWidth),
        assert(extended != null),
        assert(!extended || (labelType == null || labelType == NavigationRailLabelType.none));
複製程式碼

destinations 代表了導航條,型別是 NavigationRailDestination 陣列型別,NavigationRailDestination 就代表了每一個 item 。

類似 BottomNavigationBar , NavigationRail 導航的item 也有選中和未選中的狀態,可以設定不同的樣式。

     destinations: [
          NavigationRailDestination(
            icon: Icon(Icons.favorite_border),
            selectedIcon: Icon(Icons.favorite),
            label: Text('First'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.bookmark_border),
            selectedIcon: Icon(Icons.book),
            label: Text('Second'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.star_border),
            selectedIcon: Icon(Icons.star),
            label: Text('Third'),
          ),
        ],
複製程式碼

通常來說,每一個 item 都有三個元素: icon、selectedIcon 和 label 。

Flutter 1.17  Widget 之 NavigationRail 初探

  • icon 未被選中狀態下的圖示(通常都是輪廓)。

  • selectedIcon 可選的,選擇狀態下的圖示。

  • label 必須要有的,圖示下方的描述文字。

四、示例

class NanigationRailMain extends StatefulWidget {
  @override
  NanigationRailMainState createState() => NanigationRailMainState();
}


class NanigationRailMainState extends State<NanigationRailMain>    {
  int _selectedIndex = 0;
  final padding = 8.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xff28292E),
      body: SafeArea(
        child: Row(
          children: <Widget>[
            NavigationRail(
              minWidth: 56.0,
              groupAlignment: 1.0,
              backgroundColor: Color(0xff2D3035),
              selectedIndex: _selectedIndex,
              onDestinationSelected: (int index) {
                setState(() {
                  _selectedIndex = index;
                });
              },
              labelType: NavigationRailLabelType.all,
              leading: Column(
                children: <Widget>[
                  SizedBox(
                    height: 8,
                  ),
                  Center(
                    child: CircleAvatar(
                      radius: 16,
                      backgroundImage: NetworkImage(
                          "https://t9.baidu.com/it/u=86853839,3576305254&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1591363041&t=58576d535d450f80e9248bd0ee84c8c0"),
                    ),
                  ),
                  SizedBox(
                    height: 108,
                  ),
                  RotatedBox(
                    quarterTurns: -1,
                    child: IconButton(
                      icon: Icon(Icons.tune),
                      color: Color(0xffFCCFA8),
                      onPressed: () {},
                    ),
                  )
                ],
              ),
              selectedLabelTextStyle: TextStyle(
                color: Color(0xffFCCFA8),
                fontSize: 13,
                letterSpacing: 0.8,
                decoration: TextDecoration.underline,
                decorationThickness: 2.0,
              ),
              unselectedLabelTextStyle: TextStyle(
                fontSize: 13,
                letterSpacing: 0.8,
              ),
              destinations: [
                buildRotatedTextRailDestination("Popular", padding),
                buildRotatedTextRailDestination("Favourites", padding),
                buildRotatedTextRailDestination("Inspiration", padding),
                buildRotatedTextRailDestination("All", padding),
              ],
            ),
            // This is the main content.
            ContentSpace(_selectedIndex)
          ],
        ),
      ),
    );
  }
}

NavigationRailDestination buildRotatedTextRailDestination(
    String text, double padding) {
  return NavigationRailDestination(
    icon: SizedBox.shrink(),
    label: Padding(
      padding: EdgeInsets.symmetric(vertical: padding),
      child: RotatedBox(
        quarterTurns: -1,
        child: Text(text),
      ),
    ),
  );
}

class ContentSpace extends StatelessWidget {
  final int _selectedIndex;

  ContentSpace(this._selectedIndex);

  final List<String> images = [
    "https://images.unsplash.com/photo-1524758631624-e2822e304c36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
    "https://images.unsplash.com/photo-1493663284031-b7e3aefcae8e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
    "https://images.unsplash.com/photo-1538688525198-9b88f6f53126?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1267&q=80",
    "https://images.unsplash.com/photo-1513161455079-7dc1de15ef3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80",
    "https://images.unsplash.com/photo-1544457070-4cd773b4d71e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=843&q=80",
    "https://images.unsplash.com/photo-1532323544230-7191fd51bc1b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80",
    "https://images.unsplash.com/photo-1549488344-cbb6c34cf08b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80",
  ];

  final List<String> titles = [
    "Popular\nIdeas",
    "Favourites",
    "Inspiration\nIdeas",
    "All"
  ];

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.fromLTRB(24, 8, 0, 0),
        child: MediaQuery.removePadding(
          removeTop: true,
          context: context,
          child: ListView(
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  IconButton(
                    icon: Icon(Icons.search),
                    onPressed: () {},
                  ),
                  IconButton(
                    icon: Icon(Icons.calendar_today),
                    onPressed: () {},
                  ),
                ],
              ),
              SizedBox(
                height: 24,
              ),
              Text(titles[_selectedIndex],
                  style: TextStyle(
                    color: Color(0xffFCCFA8),
                    fontSize: 16,
                    letterSpacing: 0.8,
                    decoration: TextDecoration.underline,
                    decorationThickness: 2.0,
                  ),

              ),
              SizedBox(
                height: 24,
              ),


              for (var i in images) ImageCard(i),


            ],
          ),
        ),
      ),
    );
  }
}

class ImageCard extends StatelessWidget {
  final uri;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.fromLTRB(0, 0, 24, 24),
      child: Image.network(uri),
      clipBehavior: Clip.antiAliasWithSaveLayer,
      elevation: 0.0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),

      ),
    );
  }

  const ImageCard(this.uri);

}
複製程式碼

效果

Flutter 1.17  Widget 之 NavigationRail 初探

github

最後

歡迎關注「Flutter 程式設計開發」微信公眾號 。

Flutter 1.17  Widget 之 NavigationRail 初探

相關文章