在Flutter中构建响应式UI

473 阅读6分钟

设计师喜欢制作响应式UI,但在现实生活中实现它们并不容易。作为一名Flutter开发者,你不应该对你的设计师说不。所以让我们再次让他们开心吧。

一、TDLR

  • 在响应式UI中,我们不对尺寸和位置使用硬编码值。
  • 用于MediaQuery获取窗口的实时大小。
  • 使用FlexibleExpanded小部件获得灵活的UI,该UI使用百分比而不是硬编码值。
  • 用于LayoutBuilder获取ConstraintBox父小部件的。
  • MediaQuery您可以使用或获取是设备的方向OrientationBuilder.

二、响应式设计的组成部分是什么?

在响应式设计应用程序中,我们需要考虑三个主要原因:大小、方向和设备类型,只要其中任何一个发生变化,应用程序UI就会发生变化。

例如,如果您在纵向模式下的一列中有一个图像列表,它应该是横向模式下变成两列,以便在一页中向用户显示尽可能多的内容,在这个小例子中,用户界面取决于设备的方向,下一部分我们将通过示例讨论每个组件。

三、有什么问题?

这里的主要问题是UI小部件的尺寸和位置的硬编码值。所以要解决问题就不要使用它们,对吗 ?是好的,但这只是解决问题的一部分,因为在响应式UI中,您不只是缩放小部件,因为在响应UI中,您不只是缩放小部件,有时您会重新定位它们,有时您会根据可用空间更改它们。在这篇文章中,我将给你一些关于如何做到这一点的提示。

信息:Flutter中的度量单位是逻辑像素

3.1、屏幕尺寸不同

image.png

Flutter同时支持Android和iOS设备,并且它们都有多种尺寸,不可能为每一种设备都制作一个布局,但是可以并且非常容易地使您的一种布局比例适合所有设备,在Flutter中我们有一组小部件可以在这种情况下提供帮助。让我们谈谈其中的一些:

MediaQuery是一个为您提供有关当前媒体(也就是满足您的应用程序的窗口)的InhertedWidget信息,例如设备的大小、方向和像素比。

因此,例如,如果您有一个抽屉菜单,并且您只想在媒体宽度小于600时显示它,如果它大于600,则您将在左侧显示普通菜单。

注意 :600只是android平板电脑屏幕的示例。
Widget build(BuildContext context) {
    Size media = MediaQuery.of(context).size;
    
    return Scaffold(
        appBar: AppBar(),
        drawer: media.width < 600
            ? Drawer(
               child: Menu(),
            )
            : null,
            body: Row(
                children: <Widget>[
                    media.width > 600 ? Flexible(
                        flex: 1,
                        child: Menu()
                    )
                    : Container(),
                    Flexible(
                        flex: 3,
                        child: Center(
                            child: Text(
                                'Size ${media.width} * ${media.height}',
                                style: Theme.of(context).textTheme.title,
                            ),
                        ),
                    ),
                ],
            ),
    );
}

这个例子非常简单,我们有一个Scaffold包含一个Row,其中有两个孩子,一个是位于中间的Text,一个位于左侧的Menu,只有在条件为真时才会显示。有趣的是我声明media的第三行。

现在,如果您在平板电脑和中型Android设备上运行此应用程序,您将获得不同的用户界面:

image.png

image.png

**提示:** 使用[breakpoint](https://github.com/fluttercommunity/breakpoint)包获取有关屏幕的有用信息,并将屏幕布局分成几列,您可以根据窗口大小轻松隐藏/显示这些列

目前Android和iOS已经有了实时窗口变化,例如,我们有Multi-Window Support,在这种模式下,用户可以修改应用程序的大小,使用MediaQuery你仍然可以获得准确的大小,让我们运行以前的应用程序处于多窗口模式,正如您所看到的那样,它按预期工作。

image.png

image.png

在前面的示例中,我们不只是使用MediaQuery来获得这种灵活的设计,我们还使用了其他小部件Flexible,例如遵循FlexBox布局系统的小部件,让我们再多谈谈这个。

FlexibleExpanded是两个小部件,您可以在RowColumnFlex中使用它们,让它们的孩子可以灵活的扩展以填充可用空间,它们之间的主要区别可以在这个例子中展示:

image.png

``` Scaffold( appBar: AppBar(), body: Column( children: [ Row( children: [ buildExpanded(), buildFlexible(), ], ), Row( children: [ buildExpanded(), buildExpanded(), ], ), Row( children: [ buildFlexible(), buildFlexible(), ], ), ], ), ); ``` `buildFlexible`和`buildExpanded`只是两个函数,它们返回正确的小部件。

例如:

Widget buildFlexible() {
    return Flexible(
        child: Container(
            color: Colors.green,
            child: Text(
                "Flexible",
                style: Theme.of(context).textTheme.display1,
            ),
        ),
    );
}
注意:如果您不提供flex值,则默认值为1

所以不同之处在于Expanded小部件需要孩子填充可用空间,而Flexible则不需要。现在您可以使用它们在多个项目之间分配空间。

MediaQuery小部件将为您提供适用于您应用程序的全局大小,同时您可以将其LayoutBuilder视为本地MediaQuery更关注大小限制,因此它将为您提供父小部件的限制以读取它们并决定根据它们显示什么

image.png

如果您希望固定大小的小部件能够自行缩放和定位以适应可用空间,那么此小部件是必不可少的。看看这个例子,我有三个Text小部件,在它们的帮助下,FittedBox它们会自动缩放以适应两个方向的屏幕宽度。

image.png

image.png

专业提示: 使用flutter_device_preview包在不同的真实屏幕尺寸和不同的方向上测试你的应用程序。

1_SYVKCYt9UwrSLkDaHkO6SQ.gif

### 3.2、方向 方向变化是另一种类别,如果运行时大小发生变化,就像我们在上面的多窗口示例中看到的那样,但这次它更针对手机用户,因为大多数时候人们从纵向模式切换到横向模式以获取更多数据视图。
  • 访问设备方向

使用MediaQuery

Widget build(BuildContext context) {
    media = MediaQuery.of(context);
    
    return Scaffold(
        appBar: AppBar(),
        body: Center(
            child: Text(media.orientation.toString()),
        ),
    );
}

使用OrientationBuilder:

OrientationBuilder(
    builder: (BuilderContext context, Orientation orientation) {
        return Center(
            child: Text(orientation.toString()),
        );
    }
),
注意:这两种方式都是用相同的逻辑,即如果宽度 > 高度则他是纵向的,否则它是横向的。但`OrientationBuilder`使用父级`BoxConstraints`,所以除非你提供一个显示`maxWidth`的大于`maxHeight`你会得到正确的方向。

现在让向您展示一个简单的例子,它可以派上用场。假设您有一个如下的登录页面:

image.png

现在 ,如果您将其更改为横向,它将如下所示:

image.png

这不是您应该向用户显示的内容,用户希望在登录页面中看到输入字段以写入登录信息。让我们使用OrientationBuilder

OrientationBuilder(
    builder: (BuildContext context, Orientation orientation) {
        return ListView(
            children: <Widget>[
                FlutterLogo(
                    size: orientation == Orientation.portrait ? 200 : 100.0,
                ),
            ],
        );
    }
);

如果方向是纵向,那么修复很简单,那么大小是200.0,否则它是100.0。所以现在它看起来会好一点。

image.png

好的,这就是方向,但是如果你想看看如何以疯狂的方式处理方向,请看一下Simon Lightfoot这个实现

ezgif.com-gif-maker.gif

原文翻译博客:# Build Responsive UIs in Flutter