Flutter第五期 - 樣式、Scaffold、TabBar、底部導航

繼續我們的flutter之旅,今天學首頁怎麼寫,APP的首頁目前比較單一,都是下面幾個菜單,上面幾個菜單,或者側面一個我的,所以可以寫一個活的套用,以後就不用麻煩了。

前言:樣式自定義基本上都是一樣的,什麼圓角,什麼陰影,什麼旋轉,什麼漸變,總結一下以後別忘了。

樣式1:

import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "樣式佈局", home: new Scaffold( appBar: new AppBar( title: new Text("樣式佈局"), ), body: new Center( child: new FormTestRoute1(), ), ), ); } } class FormTestRoute1 extends StatefulWidget { @override _FormTestRouteState1 createState() => new _FormTestRouteState1(); } class _FormTestRouteState1 extends State { @override Widget build(BuildContext context) { return Padding( //上下左右各添加16像素補白 padding: EdgeInsets.all(16.0), child: Column( //顯式指定對齊方式為左對齊,排除對齊干擾 crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( //左邊添加8像素補白 padding: const EdgeInsets.only(left: 8.0), child: Text("hi yun1"), ), Padding( //上下各添加8像素補白 padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text("hi yun2"), ), Padding( // 分別指定四個方向的補白 padding: const EdgeInsets.fromLTRB(20.0, .0, 20.0, 20.0), child: Text("hi yun3"), ), DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.red, Colors.orange[700]]), //背景漸變 borderRadius: BorderRadius.circular(3.0), //3像素圓角 boxShadow: [ //陰影 BoxShadow( color: Colors.black54, offset: Offset(2.0, 2.0), blurRadius: 4.0) ]), child: Padding( padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0), child: Text( "Login", style: TextStyle(color: Colors.white), ), )), ], ), ); } }

樣式2:

import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "樣式佈局", home: new Scaffold( appBar: new AppBar( title: new Text("樣式佈局"), ), body: new Center( child: new FormTestRoute1(), ), ), ); } } class FormTestRoute1 extends StatefulWidget { @override _FormTestRouteState1 createState() => new _FormTestRouteState1(); } class _FormTestRouteState1 extends State { @override Widget build(BuildContext context) { return Padding( //上下左右各添加16像素補白 padding: EdgeInsets.all(16.0), child: Column( //顯式指定對齊方式為左對齊,排除對齊干擾 crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsets.only( top: 50.0, left: 120.0, right: 0.0, bottom: 50.0), //容器外補白 constraints: BoxConstraints.tightFor(width: 200.0, height: 150.0), //卡片大小 decoration: BoxDecoration( //背景裝飾 gradient: RadialGradient( //背景徑向漸變 colors: [Colors.red, Colors.orange], center: Alignment.topLeft, radius: .98), boxShadow: [ //卡片陰影 BoxShadow( color: Colors.black54, offset: Offset(2.0, 2.0), blurRadius: 4.0) ]), transform: Matrix4.rotationZ(.2), //卡片傾斜變換 alignment: Alignment.center, //卡片內文字居中 child: Text( //卡片文字 "5.20", style: TextStyle(color: Colors.white, fontSize: 40.0), ), ), Container( margin: EdgeInsets.all(20.0), //容器外補白 color: Colors.orange, child: Text("hi yun~"), ), Container( padding: EdgeInsets.all(20.0), //容器內補白 color: Colors.orange, child: Text("hi yun~"), ), ], ), ); } }

Scaffold:

大多數路由頁都會包含一個導航欄,有些路由頁可能會有抽屜菜單(Drawer)以及底部Tab導航菜單等。如果每個頁面都需要開發者自己手動去實現,這會是一件非常無聊的事。幸運的是,我們前面提到過,Flutter Material庫提供了一個Scaffold Widget,它是一個路由頁的骨架,可以非常容易的拼裝出一個完整的頁面。

import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "Scaffold首頁", home: new Scaffold( // appBar: new AppBar( // title: new Text("Scaffold首頁"), // ), body: new Center( child: new FormTestRoute1(), ), ), ); } } class FormTestRoute1 extends StatefulWidget { @override _FormTestRouteState1 createState() => new _FormTestRouteState1(); } class _FormTestRouteState1 extends State with SingleTickerProviderStateMixin { int _selectedIndex = 1; void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } void _onAdd() {} TabController _tabController; List tabs = ["Yun1", "Yun2", "Yun3"]; @override void initState() { // TODO: implement initState super.initState(); _tabController = TabController(length: tabs.length, vsync: this); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: new Text("Scafford首頁"), leading: Builder(builder: (context) { return IconButton( icon: Icon(Icons.dashboard, color: Colors.white), //自定義圖標 onPressed: () { // 打開抽屜菜單 Scaffold.of(context).openDrawer(); }, ); }), actions: [ IconButton( icon: Icon( Icons.share, ), onPressed: () {}, ), ], bottom: TabBar( controller: _tabController, tabs: tabs.map((e) => Tab(text: e)).toList(), ), ), drawer: new MyDrawer(), body: TabBarView( controller: _tabController, children: tabs.map((e) { return Container( alignment: Alignment.center, child: Text( e, textScaleFactor: 5, ), ); }).toList(), ), // bottomNavigationBar: BottomNavigationBar( // items: [ // BottomNavigationBarItem( // icon: Icon(Icons.home), title: new Text("Yun1")), // BottomNavigationBarItem( // icon: Icon(Icons.business), title: new Text("Yun2")), // BottomNavigationBarItem( // icon: Icon(Icons.school), title: new Text("Yun3")), // ], // currentIndex: _selectedIndex, // fixedColor: Colors.blueGrey, // onTap: _onItemTapped, // ), 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( child: Icon(Icons.add), onPressed: _onAdd, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } } // 抽屜 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: [ Padding( padding: const EdgeInsets.only(top: 38.0), child: Row( children: [ 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: [ ListTile( leading: const Icon(Icons.add), title: const Text('Add account'), ), ListTile( leading: const Icon(Icons.settings), title: const Text('Manage accounts'), ), ], ), ), ], ), ), ); } }

總結:這塊的關鍵leading,TabBar,TabBarView,BottomNavigationBar,BottomAppBar的使用,寫法都是固定的,需要相互配合,多練吧~