Flutter 20: 圖解【分享頁面】底部對話方塊
小菜在學習 Flutter 過程中需要處理一個類似 Android 中 PopupWindow 效果的分享彈框頁。看似很簡單的頁面,裡面卻有很多值得嘗試的地方。
小菜測試時主要用 ofollow,noindex"> GridView 和 BottomSheet 來實現的,當然也可以不用 GridView ,小菜簡單介紹一下這兩組 Widget 。

GridView
GridView我們都很熟悉了,是日常中最常用到的控制元件之一,小菜前段時間學習了一下 ListView 的基本用法, GridView 的用法基本相同,小菜不再多說,只提醒一個屬性,用來設定 GridView item 基本屬性。
const SliverGridDelegateWithFixedCrossAxisCount({ @required this.crossAxisCount, // 每行 item 個數 this.mainAxisSpacing = 0.0,// 列間距,即 item 左右間距 this.crossAxisSpacing = 0.0,// 行間距,即 item 上下間距 this.childAspectRatio = 1.0,// item 寬高比,預設1:1 })
Tips:注意設定 item 個數與列間距的配合,如果太大可能會造成頁面展示不全等異常情況。
BottomSheet
BottomSheet小菜理解為是從底部向上彈的工作表,主要分為兩種:
- Persistent 式工作表 :類似於一個全新的頁面,完全展示 ScaffoldState.showBottomSheet ;
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("分享頁面"), ), body: new Center( child: new Builder(builder: (BuildContext context) { return new FlatButton( onPressed: () { showBottomSheet( context: context, builder: (BuildContext context) { return _showNomalWid(context); }); }, child: new Text("我要分享"), color: Colors.blue); }), )); } Widget _showNomalWid(BuildContext context) { return new Container( //height: 320.0, //color: Colors.greenAccent, child: new GridView.builder( gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 5.0, childAspectRatio: 1.0), itemBuilder: (BuildContext context, int index) { return new Column( children: <Widget>[ new Padding( padding: EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 6.0), child: new Image.asset( 'images/${urlItems[index]}', width: 50.0, height: 50.0, fit: BoxFit.fill, ), ), new Text(nameItems[index]) ], ); }, itemCount: nameItems.length, ), ); }
1.1 若用 showBottomSheet 方式開啟工作表,同時內容 Widget 不限制寬高,效果為新開啟一個頁面,點選空白區不會消失,如圖:

1.2 若此時設定內容 Widget 寬高,會發現依舊是重新開啟一個頁面,高度從底向上佔據所設定高度,且點選空白區不會消失,如圖:

- Modal 式工作表 :是一個半透明的頁面,預設佔據螢幕一半 ScaffoldState.showModalBottomSheet 。
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("分享頁面"), ), body: new Center( child: new Builder(builder: (BuildContext context) { return new FlatButton( onPressed: () { showModalBottomSheet( context: context, builder: (BuildContext context) { return _showNomalWid(context); }); }, child: new Text("我要分享"), color: Colors.blue); }), )); }
2.1 若用 showModalBottomSheet 方式開啟工作表,同時內容 Widget 不限制寬高,效果為開啟一個半透明頁面,預設佔據螢幕一半,點選空白區工作表消失,如圖:

2.2 若此時設定內容 Widget 寬高,會發現依舊是開啟一個半透明頁面,高度從底向上佔據所設定高度,且點選空白區會消失,如圖:

2.3 若此時設定內容 Widget 資料量很多,效果如何呢,這就是小菜選擇用 GridView 的原因,在現有寬高內進行可滑動操作即可,如圖:

核心原始碼
小菜稍稍修飾了一下頁面效果,主要原始碼如下:
import 'package:flutter/material.dart'; class SharePopup extends StatelessWidget { List<String> nameItems = <String>[ '微信', '朋友圈', 'QQ', 'QQ空間', '微博', 'FaceBook', '郵件', '連結' ]; List<String> urlItems = <String>[ 'icon_wechat.png', 'icon_wechat_moments.png', 'icon_qq.png', 'icon_qzone.png', 'icon_sina.png', 'icon_facebook.png', 'icon_email.png', 'icon_copylink.png' ]; @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("分享頁面"), ), body: new Center( child: new Builder(builder: (BuildContext context) { return new FlatButton( onPressed: () { showModalBottomSheet( context: context, builder: (BuildContext context) { return _shareWidget(context); }); }, child: new Text("我要分享"), color: Colors.blue); }), )); } Widget _shareWidget(BuildContext context) { return new Container( height: 250.0, child: new Column( children: <Widget>[ new Padding( padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0), child: new Container( height: 190.0, child: new GridView.builder( gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 5.0, childAspectRatio: 1.0), itemBuilder: (BuildContext context, int index) { return new Column( children: <Widget>[ new Padding( padding: EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 6.0), child: new Image.asset( 'images/${urlItems[index]}', width: 50.0, height: 50.0, fit: BoxFit.fill, ) ), new Text(nameItems[index]) ], ); }, itemCount: nameItems.length, ), ), ), new Container( height: 0.5, color: Colors.blueGrey, ), new Center( child: new Padding( padding: EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0), child: new Text( '取消', style: new TextStyle(fontSize: 18.0, color: Colors.blueGrey), ) ), ) ], ), ); } }
小菜剛接觸 Flutter 時間不長,還有很多不清楚和不理解的地方,如果有不對的地方還希望多多指教。以下是小菜公眾號,歡迎閒來吐槽〜

公眾號.jpg