<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Scaffold、TabBar、底部導航 Material庫提供了很多Widget,本節介紹一些常用的Widget,其余的讀者可以查看文檔或Flutter Gallery中Material組件部分的示例。注意,筆者強烈建議用戶將Flutter Gallery示例跑起來,它是一個很全面的Flutter示例,是非常好的參考Demo。 ### Scaffold 大多數路由頁都會包含一個導航欄,有些路由頁可能會有抽屜菜單(Drawer)以及底部Tab導航菜單等。如果每個頁面都需要開發者自己手動去實現,這會是一件非常無聊的事。幸運的是,我們前面提到過,Flutter Material庫提供了一個Scaffold Widget,它是一個路由頁的骨架,可以非常容易的拼裝出一個完整的頁面。 ## 示例 我們實現一個頁面,它包含: 1. 一個導航欄 2. 導航欄右邊有一個分享按鈕 3. 有一個抽屜菜單 4. 有一個底部導航 5. 右下角有一個懸浮的動作按鈕 最終效果如下: ![](https://box.kancloud.cn/f83914b2c5e3f0ffc65dfab3f0fae028_360x640.png)![](https://box.kancloud.cn/a2dab018340730e6678d7f39ffa0e7ce_360x640.png) 實現代碼如下: ``` class ScaffoldRoute extends StatefulWidget { @override _ScaffoldRouteState createState() => _ScaffoldRouteState(); } class _ScaffoldRouteState extends State<ScaffoldRoute> { int _selectedIndex = 1; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( //導航欄 title: Text("App Name"), actions: <Widget>[ //導航欄右側菜單 IconButton(icon: Icon(Icons.share), onPressed: () {}), ], ), drawer: new MyDrawer(), //抽屜 bottomNavigationBar: BottomNavigationBar( // 底部導航 items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')), BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')), BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')), ], currentIndex: _selectedIndex, fixedColor: Colors.blue, onTap: _onItemTapped, ), floatingActionButton: FloatingActionButton( //懸浮按鈕 child: Icon(Icons.add), onPressed:_onAdd ), ); } void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } void _onAdd(){ } } ``` 上面代碼中我們用到了另外幾個Widget,下面我們來分別介紹一下: ### AppBar AppBar是一個Material風格的導航欄,它可以設置標題、導航欄菜單、底部Tab等。下面我們看看AppBar的定義: ``` AppBar({ Key key, this.leading, //導航欄最左側Widget,常見為抽屜菜單按鈕或返回按鈕。 this.automaticallyImplyLeading = true, //如果leading為null,是否自動實現默認的leading按鈕 this.title,// 頁面標題 this.actions, // 導航欄右側菜單 this.bottom, // 導航欄底部菜單,通常為Tab按鈕組 this.elevation = 4.0, // 導航欄陰影 this.centerTitle, //標題是否居中 this.backgroundColor, ... //其它屬性見源碼注釋 }) ``` 如果給Scaffold添加了抽屜菜單,默認情況下Scaffold會自動將AppBar的leading設置為菜單按鈕(如上面截圖所示)。如果我們想自定義菜單圖標,可以手動來設置leading,如: ``` Scaffold( appBar: AppBar( title: Text("App Name"), leading: Builder(builder: (context) { return IconButton( icon: Icon(Icons.dashboard, color: Colors.white), //自定義圖標 onPressed: () { // 打開抽屜菜單 Scaffold.of(context).openDrawer(); }, ); }), ... ) ``` 代碼運行效果: ![](https://box.kancloud.cn/ac15dcbbc9c984975120cba1d5558b9e_261x35.png) 可以看到左側菜單已經替換成功。 代碼中打開抽屜菜單的方法在ScaffoldState中,通過`Scaffold.of(context)`可以獲取父級最近的Scaffold Widget的State對象,原理可以參考本書后面“Element與BuildContext” 一章。Flutter還有一種通用的獲取StatefulWidget對象State的方法:通過GlobalKey來獲取! 步驟有兩步: 1. 給目標StatefulWidget添加GlobalKey ``` //定義一個globalKey, 由于GlobalKey要保持全局唯一性,我們使用靜態變量存儲 static GlobalKey<ScaffoldState> _globalKey= new GlobalKey(); ... Scaffold( key: _globalKey , //設置key ... ) ``` 2. 通過GlobalKey來獲取State對象 ``` _globalKey.currentState.openDrawer() ``` #### TabBar 下面我們通過“bottom”屬性來添加一個導航欄底部tab按鈕組,將要實現的效果如下: ![](https://box.kancloud.cn/946b0c4790ff769a108bedc1ba5c847c_360x195.png) Material組件庫中提供了一個TabBar組件,它可以快速生成Tab菜單,下面是上圖對應的源碼: ``` class _ScaffoldRouteState extends State<ScaffoldRoute> with SingleTickerProviderStateMixin { TabController _tabController; //需要定義一個Controller List tabs = ["新聞", "歷史", "圖片"]; @override void initState() { super.initState(); // 創建Controller _tabController = TabController(length: tabs.length, vsync: this); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( ... //省略無關代碼 bottom: TabBar( //生成Tab菜單 controller: _tabController, tabs: tabs.map((e) => Tab(text: e)).toList()), ), ... //省略無關代碼 } ``` 上面代碼首先創建了一個TabController ,它是用于控制/監聽Tab菜單切換。然后通過TabBar生成了一個底部菜單欄,TabBar的`tabs`屬性接受一個Widget數組,表示每一個Tab子菜單,我們可以自定義,也可以像示例中一樣直接使用Tab Widget,它也是Material組件庫提供的Material風格的Tab菜單。 Tab Widget有三個可選參數,除了可以指定文字外,還可以指定Tab菜單圖標,或者直接自定義Widget,定義如下: ``` Tab({ Key key, this.text, // 菜單文本 this.icon, // 菜單圖標 this.child, // 自定義Widget }) ``` 開發者可以根據實際需求來定制。 #### TabBarView 通過TabBar我們只能生成一個靜態的菜單,如果要實現Tab頁,我們可以通過TabController去監聽Tab菜單的切換去切換Tab頁,代碼如: ``` _tabController.addListener((){ switch(_tabController.index){ case 1: ...; case 2: ... ; } }); ``` 如果我們Tab頁可以滑動切換的話,還需要在滑動過程中更新TabBar指示器的偏移。顯然,要手動處理這些是很麻煩的,為此,Material庫提供了一個TabBarView組件,它可以很輕松的配合TabBar來實現同步切換和滑動狀態同步,示例如下: ``` Scaffold( appBar: AppBar( ... //省略無關代碼 bottom: TabBar( controller: _tabController, tabs: tabs.map((e) => Tab(text: e)).toList()), ), drawer: new MyDrawer(), body: TabBarView( controller: _tabController, children: tabs.map((e) { //創建3個Tab頁 return Container( alignment: Alignment.center, child: Text(e, textScaleFactor: 5), ); }).toList(), ), ... // 省略無關代碼 ) ``` 運行后效果如下: ![](https://box.kancloud.cn/e38796edf69a43afb6a9aaaa2f22eb06_360x640.png) 現在,無論是點擊導航欄Tab菜單還是在頁面上左右滑動,Tab頁面都會切換,并且Tab菜單的狀態和Tab頁面始終保持同步。下面我們來看看代碼,細心的讀者可以發現,TabBar和TabBarView的controller是同一個!正是如此,TabBar和TabBarView正是通過同一個controller來實現菜單切換和滑動狀態同步的。 另外,Material組件庫也提供了一個PageView Widget,它和TabBarView功能相似,讀者可以自行了解一下。 ### 抽屜菜單Drawer Scaffold的`drawer`和`endDrawer`屬性可以分別接受一個Widget作為頁面的左、右抽屜菜單,如果開發者提供了抽屜菜單,那么當用戶手指重屏幕左/右向里滑動時便可打開抽屜菜單。本節開始部分的示例中實現了一個左抽屜菜單MyDrawer,源碼如下: ``` class MyDrawer extends StatelessWidget { const MyDrawer({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Drawer( child: MediaQuery.removePadding( context: context, // DrawerHeader consumes top MediaQuery padding. removeTop: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Padding( padding: const EdgeInsets.only(top: 38.0), child: Row( children: <Widget>[ Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: ClipOval( child: Image.asset( "imgs/avatar.png", width: 80, ), ), ), Text( "Wendux", style: TextStyle(fontWeight: FontWeight.bold), ) ], ), ), Expanded( child: ListView( children: <Widget>[ ListTile( leading: const Icon(Icons.add), title: const Text('Add account'), ), ListTile( leading: const Icon(Icons.settings), title: const Text('Manage accounts'), ), ], ), ), ], ), ), ); } } ``` 抽屜菜單通常將Drawer作為根節點,它實現了Material風格的菜單面板,`MediaQuery.removePadding`可以移除抽Drawer內的一些指定空白,讀者可以嘗試傳遞不同的參數來看看實際效果。抽屜菜單頁頂部由用戶頭像和昵稱組成,底部是一個菜單列表,用ListView實現,關于ListView我們將在后面“可滾動Widget”一節詳細介紹。 ### FloatingActionButton FloatingActionButton是Material設計規范中的一種特殊Button,通常懸浮在頁面的某一個位置作為某種常用動作的快捷入口,如本節示例中頁面右下角的"?"號按鈕。我們可以通過Scaffold的`floatingActionButton`屬性來設置一個FloatingActionButton,同時通過`floatingActionButtonLocation`屬性來指定其在頁面中懸浮的位置,這個比較簡單,不在贅述。 ### 底部Tab導航欄 我們可以通過Scaffold的`bottomNavigationBar`屬性來設置底部導航,如本節開始示例所示,我們通過Material組件庫提供的BottomNavigationBar和BottomNavigationBarItem兩個Widget來實現Material風格的底部導航欄,可以看到代碼非常簡單,不在贅述。但是如果我們想實現如下效果的底部導航應該怎么做呢? ![](https://box.kancloud.cn/9b16ea455b9e1c2222e0f0c3b3658676_360x640.png) Material組件庫中提供了一個BottomAppBar Widget,可以和FloatingActionButton配合實現這種"打洞"效果。源碼如下: ``` bottomNavigationBar: BottomAppBar( color: Colors.white, shape: CircularNotchedRectangle(), // 底部導航欄打一個圓形的洞 child: Row( children: [ IconButton(icon: Icon(Icons.home)), SizedBox(), //中間位置空出 IconButton(icon: Icon(Icons.business)), ], mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部導航欄橫向空間 ), ) ``` 可以看到,上面代碼中沒有控制打洞位置的屬性,實際上,打洞的位置取決于FloatingActionButton的位置,上面FloatingActionButton的位置為: ``` floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ``` BottomAppBar的`shape`屬性決定洞的外形,CircularNotchedRectangle實現了一個圓形的外形,我們也可以自定義外形,比如,Flutter Gallery示例中就有一個”鉆石“形狀的實現,讀者感興趣可以自行查看。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看