Flutter 學(xué)習(xí)之旅(三十) 對話框Dialog

AlertDialog

先來看一下AlertDialog的構(gòu)造方法

AlertDialog({
    Key key,
///標(biāo)題
    this.title,
///標(biāo)題padding
    this.titlePadding,
///標(biāo)題style
    this.titleTextStyle,
///內(nèi)容
    this.content,
///padding
    this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
///style
    this.contentTextStyle,
/// 動作 widget 列表,
    this.actions,
    this.actionsPadding = EdgeInsets.zero,
///action 溢出 方向
    this.actionsOverflowDirection,
/// action 溢出水平間距離
    this.actionsOverflowButtonSpacing,
///按鈕padding
    this.buttonPadding,
///背景色
    this.backgroundColor,
///陰影
    this.elevation,
    this.semanticLabel,
    this.insetPadding = _defaultInsetPadding,
    this.clipBehavior = Clip.none,
    this.shape,
///不可滾動,想要滾動需要嵌套SingleChildScrollView
    this.scrollable = false,
  })

再來看一下創(chuàng)建dialog的方法

Future<T> showDialog<T>({
  @required BuildContext context,
  WidgetBuilder builder,
///點擊外部是否消失
  bool barrierDismissible = true,
///外部陰影顏色
  Color barrierColor,
///是否使用安全區(qū)域
  bool useSafeArea = true,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
  @Deprecated(
    'Instead of using the "child" argument, return the child from a closure '
    'provided to the "builder" argument. This will ensure that the BuildContext '
    'is appropriate for widgets built in the dialog. '
    'This feature was deprecated after v0.2.3.'
  )
  Widget child,
})

例子

Future<int> _getAlertDialog(BuildContext context) {
  return showDialog<int>(
      context: context,
      builder: (context) => AlertDialog(
            title: Text('Title'),

            ///如果內(nèi)容過長,需要使用SingleChildScrollView
            content: Scrollbar(
              child: SingleChildScrollView(
                physics: BouncingScrollPhysics(),
                child: Text(
                    "這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容..." *
                        20),
              ),
            ),
            actions: [
              FlatButton(
                child: Text('取消'),
                onPressed: () => Navigator.of(context).pop(0),
              ),
              FlatButton(
                child: Text('確定'),
                onPressed: () => Navigator.of(context).pop(1),
              )
            ],
          ),

      ///點擊dialog 外部是否消失
      barrierDismissible: true);
}
e6320b087051ba63bf108a8c91ee047.jpg

Future<T> showDialog<T>

showDialog<T>()是Material組件庫提供的一個用于彈出Material風(fēng)格對話框的方法,并返回int類型,取消的時候返回0,確定的時候返回1,比較簡單,
這里唯一需要注意的是關(guān)閉dialog 方法, Navigator.of(context).pop({Object result}),這個和路由管理關(guān)閉頁面的方式是一樣的

Future<T> showDialog<T>({
  @required BuildContext context,
  WidgetBuilder builder,
  bool barrierDismissible = true,
  Color barrierColor,
  bool useSafeArea = true,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
  @Deprecated(
    'Instead of using the "child" argument, return the child from a closure '
    'provided to the "builder" argument. This will ensure that the BuildContext '
    'is appropriate for widgets built in the dialog. '
    'This feature was deprecated after v0.2.3.'
  )
  Widget child,
}) {
  assert(child == null || builder == null);
  assert(barrierDismissible != null);
  assert(useSafeArea != null);
  assert(useRootNavigator != null);
  assert(debugCheckHasMaterialLocalizations(context));

  final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
  return showGeneralDialog(
    context: context,
    pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
      final Widget pageChild = child ?? Builder(builder: builder);
      Widget dialog = Builder(
        builder: (BuildContext context) {
          return theme != null
            ? Theme(data: theme, child: pageChild)
            : pageChild;
        }
      );
      if (useSafeArea) {
        dialog = SafeArea(child: dialog);
      }
      return dialog;
    },
    barrierDismissible: barrierDismissible,
    barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
    barrierColor: barrierColor ?? Colors.black54,
    transitionDuration: const Duration(milliseconds: 150),
    transitionBuilder: _buildMaterialDialogTransitions,
    useRootNavigator: useRootNavigator,
    routeSettings: routeSettings,
  );
}

看了一下showDialog 的源碼,是對showGeneralDialog這個方法進(jìn)行了包裝,發(fā)現(xiàn)了如果使用了MaterialAppTheme 則該dialog使用 MaterialAppTheme , 其他的都是固定封裝,

Future<T> showGeneralDialog<T>

Future<T> showGeneralDialog<T>({
  @required BuildContext context,
  @required RoutePageBuilder pageBuilder,
  bool barrierDismissible,
  String barrierLabel,
  Color barrierColor,
  Duration transitionDuration,
  RouteTransitionsBuilder transitionBuilder,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
}) {
  assert(pageBuilder != null);
  assert(useRootNavigator != null);
  assert(!barrierDismissible || barrierLabel != null);
  return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(_DialogRoute<T>(
    pageBuilder: pageBuilder,
    barrierDismissible: barrierDismissible,
    barrierLabel: barrierLabel,
    barrierColor: barrierColor,
    transitionDuration: transitionDuration,
    transitionBuilder: transitionBuilder,
    settings: routeSettings,
  ));
}

而 showGeneralDialog 則是使用 Navigator.push了一個 _DialogRoute ,這里也就解釋了關(guān)閉dialog 為什么是
Navigator.pop的方法了,

這個例子我為了演示嵌套滑動的效果,我使用SingleChildScrollView

但是我覺得現(xiàn)在大部分dialog的設(shè)計應(yīng)該是這樣的


image.png

這種方式如果使用actions 和content 來寫的話,想到什么辦法,只能利用content 來寫所有的布局

image.png

這是我利用content寫出來的結(jié)果,

Future<int> _getAlertDialog(BuildContext context) {
  return showDialog<int>(
      context: context,
      builder: (context) => AlertDialog(
//            title: Container(
//              alignment: Alignment.center,
//              child: Text('Title'),
//            ),
            /// 修改內(nèi)容邊距
            contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
            ///  修改圓角大小
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(12))),

            ///如果內(nèi)容過長,需要使用 SingleChildScrollView
            content: SizedBox(
              height: 180,
              child: Column(
                children: [
                  SizedBox(
                    height: 120,
                    ///包裹內(nèi)容,為了使滑動開始位置是從距離30的位置開始
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
                      child: Scrollbar(
                        child: SingleChildScrollView(
                          physics: BouncingScrollPhysics(),
                          child:  Text(
                              "這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容...這個是內(nèi)容..." *
                                  20),
                        ),
                      ),
                    )
                  ),
                  /// 水平橫線
                  Divider(
                    height: 10,
                  ),
                  /// 為了均分按鈕 ,
                  Row(
                    children: [
                      Expanded(
                        flex: 1,
                        child: FlatButton(
                          child: Text(
                            '取消',
                            style:
                                TextStyle(color: Colors.black87, fontSize: 18),
                          ),
                          onPressed: () => Navigator.of(context).pop(0),
                        ),
                      ),
                      SizedBox(
                        height: 30,
                        child:VerticalDivider(
                          width:10,
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: FlatButton(
                          child: Text(
                            '確定',
                            style: TextStyle(
                                color: Colors.blueAccent, fontSize: 18),
                          ),
                          onPressed: () => Navigator.of(context).pop(1),
                        ),
                      )
                    ],
                  ),
                ],
              ),
            ),
          ),

      ///點擊dialog 外部是否消失
      barrierDismissible: true);
}

其實這中類似ios樣式的控件Flutter在設(shè)計之初也想到了,在Cupertino 組件中就有CupertinoDialog ,不過到了現(xiàn)在版本已經(jīng)被放棄了,使用CupertinoAlertDialog替代了CupertinoDialog 使用方式和dialog是一致的,說實話android機(jī)器上顯示的這個dialog 確實和ios的效果沒啥差別,官方合適很良心的

例子

 getCupertinoAlertDialog(BuildContext context)  {
  return showDialog<int>(context: context,builder: (con){
    return  CupertinoAlertDialog(
      title: Text("This is Title"),
      content: Text('This is content'*10),
      actions: [
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(0);
          },
          child: Text('取消'),
        ),
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(0);
          },
          child: Text('中間的'),
        ),
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(1);
          },
          child: Text('確定'),
        ),
      ],
    );
  });
  }

效果圖


image.png
image.png

SimpleDialog

再開看一下他的構(gòu)造方法

SimpleDialog({
    Key key,
    this.title,
    this.titlePadding = const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
    this.titleTextStyle,
    this.children,
    this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
    this.backgroundColor,
    this.elevation,
    this.semanticLabel,
    this.shape,
  }) 

例子

_getSimpleDialog(BuildContext context) async {
  var lan = await showDialog<int>(
    context: context,
    builder: (context) => SimpleDialog(
      title: Text('Title'),
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 6),
          child: SimpleDialogOption(
            child: Text('這個是第一個'),
            onPressed: () => Navigator.of(context).pop(0),
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 6),
          child: SimpleDialogOption(
            child: Text('這個是第二個'),
            onPressed: () => Navigator.of(context).pop(1),
          ),
        )
      ],
    ),
  );
  printString('language is ${lan}');
}

效果圖


image.png

這里的子條目使用的是SimpleDialogOption ,他相當(dāng)如一個button

showModalBottomSheet

在介紹Scaffold 的時候介紹過showModalBottomSheet 創(chuàng)建一個類似bottomsheetdialog,

這里寫一個日期選擇
先來樣式


image.png
  void selectDate(BuildContext context) async {
    DateTime dateTime = DateTime.now();

    var date = await showModalBottomSheet<DateTime>(
      context: context,
      builder: (context) => Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            children: [
              Expanded(
                  flex: 1,
                  child: GestureDetector(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(19),
                      child: Text('取消'),
                    ),
                  )),
              Expanded(
                  flex: 1,
                  child: GestureDetector(
                    onTap: () {
                      Navigator.of(context).pop(dateTime);
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(10),
                      child: Text('確定', textDirection: TextDirection.rtl),
                    ),
                  )),
            ],
          ),
          SizedBox(
            height: 200,
            child: CupertinoDatePicker(
              onDateTimeChanged: (date) {
                dateTime = date;
              },
              mode: CupertinoDatePickerMode.date,
              maximumDate: DateTime.now().add(Duration(days: 20)),
              minimumDate: DateTime.now().add(Duration(days: -20)),
            ),
          )
        ],
      ),
    );

    printString('date:${date ?? ''}');
  }

我學(xué)習(xí)flutter的整個過程都記錄在里面了
http://www.reibang.com/c/36554cb4c804

最后附上demo 地址

https://github.com/tsm19911014/tsm_flutter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拜效,一起剝皮案震驚了整個濱河市叹哭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖闸溃,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潘拱,死亡現(xiàn)場離奇詭異揩瞪,居然都是意外死亡赋朦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門李破,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宠哄,“玉大人,你說我怎么就攤上這事嗤攻∶担” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵妇菱,是天一觀的道長承粤。 經(jīng)常有香客問我,道長恶耽,這世上最難降的妖魔是什么密任? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮偷俭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缰盏。我一直安慰自己涌萤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布口猜。 她就那樣靜靜地躺著负溪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪济炎。 梳的紋絲不亂的頭發(fā)上川抡,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音须尚,去河邊找鬼崖堤。 笑死,一個胖子當(dāng)著我的面吹牛耐床,可吹牛的內(nèi)容都是我干的密幔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撩轰,長吁一口氣:“原來是場噩夢啊……” “哼胯甩!你這毒婦竟也來了昧廷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤偎箫,失蹤者是張志新(化名)和其女友劉穎木柬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淹办,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡弄诲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娇唯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齐遵。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塔插,靈堂內(nèi)的尸體忽然破棺而出梗摇,到底是詐尸還是另有隱情,我是刑警寧澤想许,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布伶授,位于F島的核電站,受9級特大地震影響流纹,放射性物質(zhì)發(fā)生泄漏糜烹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一漱凝、第九天 我趴在偏房一處隱蔽的房頂上張望疮蹦。 院中可真熱鬧,春花似錦茸炒、人聲如沸愕乎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽感论。三九已至,卻和暖如春紊册,著一層夾襖步出監(jiān)牢的瞬間比肄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工囊陡, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留芳绩,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓关斜,卻偏偏與公主長得像示括,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子痢畜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容