Flutter 构建响应式 UI

1,408 阅读2分钟

在Flutter构建响应式UI非常的简单且快速,本篇文章将介绍两种方法。

在网页上页面响应式是非常常见的需求,在Flutter中也一样,用户可能会用手机或者平板来访问你的应用,如果你的应用支持了桌面端,在桌面端中也需要支持响应式。不过在Flutter中,实现响应式并不难,这篇文章将使用MediaQueryLayoutBuilder来实现,如果你有想深入学习的想法,可以去官方文档中继续学习


查看Youtube的响应式

当屏幕宽度很宽(左右两列)当屏幕宽度变窄(单列布局)
desktop.pngmobile.png

可以发现屏幕宽度在非常宽时,整体是两列的布局,宽度变窄后就是一列。

开始实现

1.MediaQuery

使用MediaQuery可以很简单的获取到当前屏幕的宽度,下面程序是获取宽度然后进行判断,如果宽度小于600则设置背景颜色为绿色,大于600为蓝色。在常见的尺寸中,小于600的宽度一般来说都可以归类到手机设备。大于600则是平板或者PC。当然,更详细的区分可以参考一些尺寸网站,本篇文章就以600为分界值。

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    var width = MediaQuery.of(context).size.width;
    return Scaffold(
      backgroundColor:
          width < 600 ? Colors.green.shade200 : Colors.blue.shade200,
      body: Center(
        child: Text(width.toStringAsFixed(2)),
      ),
    );
  }
}
小于600大于600
小于600.png大于600.png

可以看到使用MediaQuery可以非常简单且快速的就能获取到宽度进行判断,但是这种方式在应对多种UI需要响应式的时候有些麻烦,需要封装一下才会比较好用,本篇关于MediaQuery就介绍到这里。接下来将使用LayoutBuilder并封装一个组件


2.LayoutBuilder

使用LayoutBuilder的时候会被要求需要实现它的Builder方法,Builder方法会提供两个参数给我们,一个是BuildContext,另外一个是当前布局位置所获得的上层给予的布局约束。所以使用它制作响应式的时候就需要注意控件所处的位置以及父级的约束。

首先我创建了一个response_widget.dart文件用来封装LayoutBuilder方便后续使用

class ResponseWidget extends StatelessWidget {
  const ResponseWidget({
    super.key,
    required this.mobileWidget,
    required this.desktopWidget,
  });
  final Widget mobileWidget;
  final Widget desktopWidget;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        var width = constraints.maxWidth;
        if (width < 600) {
          return mobileWidget;
        } else {
          return desktopWidget;
        }
      },
    );
  }
}

在这个文件中我要求在使用它的时候传递一个mobileWidget和desktopWidget,然后在buildr内根据宽度返回不同的widget,从而实现响应式。如果你有更多的屏幕尺寸需要适配,可以在这个文件增加参数以及对应的处理条件。

现在就可以使用了

1.HomePage

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const ResponseWidget(
      mobileWidget: MobileWidget(),
      desktopWidget: DesktopWidget(),
    );
  }
}

2.MobileWidget


class MobileWidget extends StatelessWidget {
  const MobileWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green.shade100,
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Mobile Widget"),
        backgroundColor: Colors.green,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Container(
              height: 150,
              width: double.infinity,
              decoration: BoxDecoration(
                color: Colors.green.shade400,
                borderRadius: BorderRadius.circular(8),
              ),
            ),
            const SizedBox(height: 16),
            Expanded(
              child: ListView.builder(
                itemBuilder: (context, index) => Container(
                  height: 80,
                  width: double.infinity,
                  margin: const EdgeInsets.only(bottom: 16),
                  decoration: BoxDecoration(
                    color: Colors.green.shade300,
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

3.DesktopWidget

class DesktopWidget extends StatelessWidget {
  const DesktopWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green.shade100,
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Desktop Widget"),
        backgroundColor: Colors.green,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Row(
          children: [
            Expanded(
              child: Column(
                children: [
                  Container(
                    height: 150,
                    width: double.infinity,
                    decoration: BoxDecoration(
                      color: Colors.green.shade400,
                      borderRadius: BorderRadius.circular(8),
                    ),
                  ),
                  const SizedBox(height: 16),
                  Expanded(
                    child: ListView.builder(
                      itemBuilder: (context, index) => Container(
                        height: 80,
                        width: double.infinity,
                        margin: const EdgeInsets.only(bottom: 16),
                        decoration: BoxDecoration(
                          color: Colors.green.shade300,
                          borderRadius: BorderRadius.circular(8),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(width: 16),
            Container(
              width: 150,
              height: double.infinity,
              decoration: BoxDecoration(
                color: Colors.green.shade400,
                borderRadius: BorderRadius.circular(8),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

最终效果

MobileWidgetDesktopWidget
mobile_widget.pngdesktop_widget.png

RESULT.gif

源码链接:https://github.com/coderghl/flutter_responsive_ui

最后谢谢您的阅读!