Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

本头条核心宗旨

欢迎来到「技术刚刚好」作者,「技术刚刚好」是个人维护,每天至少更新一篇Flutter技术文章,实时为大家播报Flutter最新消息。如果你刚好也在关注Flutter这门技术,那就跟我一起学习进步吧,你的赞,收藏,转发是对我个人最大的支持,维护不易,欢迎关注。

技术刚刚好经历

近几年,移动端跨平台开发技术层出不穷,从Facebook家的ReactNative,到阿里家WEEX,前端技术在移动端跨平台开发中大展身手,技术刚刚好作为一名Android开发,经历了从Reactjs到Vuejs的不断学习。而在2018年,我们的主角变成了Flutter,这是Goolge开源的一个移动端跨平台解决方案,可以快速开发精美的移动App。希望跟大家一起学习,一起进步!

本文核心要点

本文会带大家学习布局调整的问题,我会以一个具体的实例来讲解本文的内容,首先会写一段代码引出本文要讲解的问题,然后在带大家一起解决它,干货满满,欢迎点赞,收藏。

在我工作当中有一次突然遇到这样一个问题,我们将在这篇文章当中来讨论并解决它。我将带大家用Flutter文档和 dart 带的工具,教大家来解决问题。

本文会学习BoxConstraints,ModalBottomSheet,Align,StackPositioned的一些窗口小部件。

我相信大家做移动APP开发从底部弹出一个菜单已经并不陌生了。在Flutter当中已经提供了一个API来。showModalBottomSheet方法。


Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

这种对话框我们项目用到的比较多,所以我们应该创建一个类来封装它。

import 'package:flutter/material.dart';

Future showBottomDialog({
  @required BuildContext context,
  String title,
  String content,
  Widget titleWidget,
  Widget contentWidget,
  List actions,
  bool allowBackNavigation = false,
}) {
  assert(title != null || titleWidget != null,
      'title and titleWidget both must not be null');
  assert(content != null || contentWidget != null,
      'content and contentWidget both must not be null');

  final theme = Theme.of(context);

  return showModalBottomSheet(
    context: context,
    shape: RoundedRectangleBorder(
      borderRadius: const BorderRadius.only(
        topLeft: Radius.circular(24),
        topRight: Radius.circular(24),
      ),
    ),
    isDismissible: allowBackNavigation,
    builder: (context) => WillPopScope(
      onWillPop: () async => allowBackNavigation,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            titleWidget ??
                Text(
                  title,
                  textAlign: TextAlign.left,
                  style: Theme.of(context).textTheme.headline2,
                ),
            SizedBox(height: 16),
            contentWidget ??
                Text(
                  content,
                  textAlign: TextAlign.left,
                  style: Theme.of(context)
                      .textTheme
                      .bodyText2
                      .copyWith(height: 1.5),
                ),
            SizedBox(height: 48),
            if (actions != null)
              ...actions
            else
              OutlineButton(
                child: Text('GOT IT!'),
                borderSide: BorderSide(color: theme.primaryColor),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
          ],
        ),
      ),
    ),
  );
}
然后,使用该包装器函数变得很容易,如下所示:

class DialogExample extends StatelessWidget {
  void _showDialog(BuildContext context) {
    showBottomDialog(
      context: context,
      allowBackNavigation: true,
      title: 'Do you wish to purchase add-ons?',
      content:
          'Add-ons help you save some extra money when you purchase them along with our original products. Plus, they help your chances of winning as well.',
      actions: [
        RaisedButton(
          child: Text('YES, GO AHEAD'),
          onPressed: () {},
        ),
        FlatButton(
          child: Text('SKIP'),
          onPressed: () {},
        )
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: RaisedButton(
          child: Text('Show Dialog'),
          onPressed: () => _showDialog(context),
        ),
      ),
    );
  }
}

这个时候我们就遇到一个问题了,这个时候我们要添加一个IconButton来关闭对话框,这个时候我们可以定义一个isDismissable:true来设置该对话框的关闭或者打开。然后添加一个按钮。

Stack(
    children : [
      Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          titleText,
          contentText,
          if(_mutipleCTAButtons_ != null) ..._multipleCTAButtons,
          else commonCTAButton
        ]
      ),
      if(closeButton)
        Align(
            alignment : Alignment.topRight,
            child : IconButton(
              icon: Icon(
                Icons.close,
                size: 24,
              ),
              onPressed: onClose ?? () => Navigator.pop(context),
            )
        )
    ]
)

showBottomDialog函数的更改如下

Future showBottomDialog({
  @required BuildContext context,
  String title,
  String content,
  Widget titleWidget,
  Widget contentWidget,
  List actions,
  bool allowBackNavigation = false,
  bool showCloseButton = false,
  Function onClose,
}) {
  assert(title != null || titleWidget != null,
      'title and titleWidget must not both be null');
  assert(content != null || contentWidget != null,
      'content and contentWidget must not both be null');

  final theme = Theme.of(context);

  return showModalBottomSheet(
    ...
    builder: (context) => WillPopScope(
      onWillPop: () async => allowBackNavigation,
      child: Stack(
        children: [
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
            child: Column(
              ...
              ...
              // Removed for brevity
              ],
            ),
          ),
          if (showCloseButton)
            Align(
              alignment : Alignment.topRight,
              child: IconButton(
                icon: Icon(Icons.close),
                onPressed: onClose ?? () => Navigator.pop(context),
              ),
            ),
        ],
      ),
    ),
  );
}

然后使用示例上面的使用方面的更改变为:

class DialogExample extends StatelessWidget {
  void _showDialog(BuildContext context) {
    showBottomDialog(
      ...
      ...
      showCloseButton : true,
    );
  }

}

这个时候显示效果如下

Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

这个时候问题就出现了,布局占据了屏幕的高度,差不多屏幕一半高了,是什么原因了,这个时候我们用Dart开发工具来找问题。

首先让我们看看哪个Widget通过打开Dart DevTools导致BottomSheet的高度增加,而无需假定它必须与IconButton对齐,因为添加它是导致问题的原因,对吗?

Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

高亮区域代表我们的BottomSheet的布局层次结构。让我们在DevTool中打开“ 选择小部件”模式,然后开始选择小部件,以查看哪个小部件占用了布局空间。

Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

阅读Flutter文档

在开始阅读文档之前,我们先来看一下StackDevTools中的尺寸(宽度和高度)。

Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

可以看出,这些尺寸是具有约束条件的,而我们有没有设置约束,所以其中minWidth和maxWidth分别设置为和分别等于screenWidth和不设置为“ 0”和“ screenWidth”(即0.0<=w<=414),这意味着明确要求它占据整个屏幕宽度,而不考虑其子级的宽度。而且,maxHeight精确地设置为9/16的screenHeight。结果,Stack的大小被设置为w = 414(screenWidth)和h = 504(screenHeight),这意味着它占据了允许的整个高度。

因此,我们的BottomSheet约束设置如下:

final minWidth = constraints.maxWidth;
final maxWidth = constraints.maxWidth
Flutter 小技巧 底部弹窗 布局大小调整 Android iOS 可运行

展开阅读全文

页面更新:2024-03-11

标签:大展   布局   对话框   宽度   部件   函数   尺寸   高度   屏幕   本文   大小   核心   文档   收藏   数码   平台   技术

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top