使用 Flutter 创建自适应应用
Flutter 已被广泛采用,因为它可以灵活地构建应用程序,以使用一个代码库在您的 Android、iOS、macOS 和 Windows 机器上运行。 在大多数情况下,这些设备具有不同的尺寸,尽管 Flutter 具有跨平台功能,但您不能保证应用程序的界面默认情况下会在这些设备上按预期呈现。 这是您在开发过程中必须考虑的功能。
本文将演示如何使用 Flutter 创建自适应应用。 您将学习如何构建适应给定设备尺寸的基本电子商务应用程序,而无需随着屏幕尺寸的变化重新构建应用程序。 我们将介绍:
什么是自适应应用程序?
构建自适应应用程序时要考虑什么
使用颤振 LayoutBuilder创建自适应应用程序
项目概况
教程:使用 Flutter 创建自适应电子商务应用
配置
建设 Product小部件
构建导航抽屉
渲染手机屏幕
渲染桌面屏幕
实施 LayoutBuilder班级
什么是自适应应用程序?
当应用程序设计为在运行时根据特定参数更改其属性值以响应不同的输入或条件时,它被认为是自适应的。 例如,自适应应用程序使用相同的代码库在移动和桌面视图上呈现不同的界面。
重要的是要注意自适应设计不应与 响应式设计 相混淆。 响应式应用程序会根据可用的屏幕尺寸动态更改其界面布局,但会保持布局设计。 应用程序的内容只是重新排列以适应屏幕大小。
另一方面,自适应设计要求您创建特定于平台的设计、内容和模板。 也就是说,您的应用程序在移动设备上的设计在更大的屏幕(例如桌面设备)上会有所不同。
构建自适应应用程序时要考虑什么
为了改善用户体验,您应该设计您的应用程序以适应具有不同设计、尺寸、形状和操作系统的不同设备(手机、可折叠设备、平板电脑、智能手表和 PC)。 您的用户应该能够在这些不同的设备上探索您的应用程序,并且仍然享受设备和操作系统的原生体验。
您在设计或开发阶段必须做出的一个重要决定是确定应用程序应该切换其布局的阈值。 在 Android 文档的一部分中, 有一组推荐值,可帮助您决定应用程序在不同屏幕尺寸上的布局。
的影响,屏幕的可用宽度可能比可用高度更重要 在大多数情况下,由于垂直滚动 。 您还需要考虑每个平台特有的鼠标、键盘、触摸输入和其他特性。 您应该自定义应用程序的体验和界面以适应主机设备。
使用颤振 LayoutBuilder创建自适应应用程序
Flutter 提供了 许多 可以在构建自适应应用程序时使用的小部件。 还有一些外部包,例如 Flutter 自适应 UI 和 自适应布局 ,您可以将它们集成到您的应用程序中。 在本文中,我们将重点介绍使用 Flutter 提供的 LayoutBuilder widget构建自适应应用程序。
这 LayoutBuilder类对于构建响应式和自适应应用程序很有用,因为它有一个在布局时调用的构建器函数。 此函数返回一个小部件,接收应用程序上下文和约束,并根据约束的值执行特定操作。 例如,当设备宽度满足通过 constraints.maxWidth财产:
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →
LayoutBuilder(builder: (ctx, constraints) {if (constraints.maxWidth < 600) {return widget;}else{return anotherWidget;}});
项目概况
本教程的演示应用程序是一个电子商务应用程序,它在可滚动视图中显示其产品。 在移动设备上,每个产品都会占用可用的宽度,用户可以垂直滚动屏幕以查看每个可用的产品。 移动视图还将包含一个带有导航按钮的应用程序抽屉。
在更大的屏幕上,例如在桌面设备上,产品分布在屏幕的宽度和高度上。 桌面视图还将在屏幕顶部包含一个导航栏,以替换移动视图中的应用程序抽屉。
如前所述,应用程序将使用 Flutter LayoutBuilder class确保它在运行时根据可用的屏幕大小呈现指定的界面,而无需重新构建应用程序。
这是应用程序在不同屏幕尺寸上的预期输出:
先决条件
基础知识 Flutter
上 Flutter SDK 安装在你的机器
您选择的任何合适的 IDE; 我将使用 Android Studio 进行此演示
教程:使用 Flutter 创建自适应电子商务应用
配置
打开您的终端并运行以下命令以创建引导的 Flutter 项目,然后在您首选的 IDE 中打开该项目。
flutter create shop_app
在里面 lib文件夹,创建两个包—— widgets和 screens— 将分别包含应用程序小部件和屏幕的 Dart 文件。
创建一个 dummy_data.dart文件中 lib文件夹,然后将 本教程的 GitHub 存储库中的内容 复制到该文件中。 您将使用此文件生成 Product后续部分中的小部件。
来自 LogRocket 的更多精彩文章:
不要错过 The Replay 来自 LogRocket 的精选时事通讯
使用 React 的 useEffect 优化应用程序的性能
之间切换 在多个 Node 版本
了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画
探索 Tauri ,一个用于构建二进制文件的新框架
比较 NestJS 与 Express.js
发现 TypeScript 领域中使用的流行 ORM
建设 Product小部件
这 Product小部件代表应用程序上每个产品项目的模板。 在里面 widgets打包,创建一个 product.dart文件并添加以下代码:
import 'package:flutter/material.dart'; class Product extends StatelessWidget {final String id;final String name;final Color color;final String imageUrl;final double price; const Product({required this.id,required this.name,this.color = Colors.amberAccent,required this.imageUrl,required this.price}); }
这里是 build小部件的方法:
@overrideWidget build(BuildContext context) {return InkWell(onTap: () => (){},child: Card(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),elevation: 4,margin: EdgeInsets.fromLTRB(15, 20, 15, 20),child: Column(children: [Expanded(child: Stack(children: [ClipRRect(borderRadius: const BorderRadius.only(topLeft: Radius.circular(15),topRight: Radius.circular(15)),child: Image.network(imageUrl,height: 250,width: double.infinity,fit: BoxFit.cover,),),Positioned(bottom: 20,right: 10,child: Container(color: Colors.black54,width: 300,padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),child: Text(name,style: const TextStyle(fontSize: 18,color: Colors.white,fontWeight: FontWeight.bold),softWrap: true,overflow: TextOverflow.fade,),),)],),),Padding(padding: const EdgeInsets.all(20.0),child:Row(children: [const Icon(Icons.attach_money),const SizedBox(width: 6),Text('$price')],),),],),),);}
上面的小部件使用了 Flutter 类和小部件的组合,包括:
InkWell 类 增强应用程序用户体验的
将 叠放的堆栈 小部件相互
的 Positioned小部件 将产品名称放置在特定位置
这是输出 Product小部件:
构建导航抽屉
将 导航抽屉 专门用于应用程序的移动视图中; 也就是说,应用程序将在移动设备上打开时呈现抽屉。 内 widgets打包,创建一个名为的 Dart 文件 navigation_drawer并在文件中包含以下代码:
import 'package:flutter/material.dart'; class NavigationDrawer extends StatelessWidget {const NavigationDrawer({Key? key}) : super(key: key); Widget buildListTile(String title, IconData icon){return ListTile(leading: Icon(icon,size: 26,),title: Text(title,style: const TextStyle(fontSize: 14,fontWeight: FontWeight.bold),),onTap: () {},);} @overrideWidget build(BuildContext context) {return Drawer(child: Column(children: [Container(height: 100,padding: EdgeInsets.all(20),alignment: Alignment.bottomCenter,color: Theme.of(context).accentColor,child: Text('App Menu',style: TextStyle(fontWeight: FontWeight.w900,fontSize: 20,color: Theme.of(context).primaryColor,),),),const SizedBox(height: 20),buildListTile('Home', Icons.home),buildListTile('About', Icons.info_outline),buildListTile('Contact', Icons.contact_phone_outlined),buildListTile('Order', Icons.shopping_cart),],),);} }
从上面的代码中, buildListTile方法为抽屉中的特定项目构建一个 ListTile 小部件 ,在这种情况下, Home, About, Contact, 和 Order.
国外小游戏网站(crazygames.com),游戏数量超丰富,全程无卡顿超快!
渲染手机屏幕
Next, create the screen to render the mobile view of the application. In the screens package, create a Dart file with the name mobile_product_view并在文件中包含以下代码片段:
import 'package:flutter/material.dart'; import 'package:shop_app/widgets/product.dart'; import '../dummy_data.dart'; import '../widgets/navigation_drawer.dart'; class MobileProductView extends StatelessWidget {final String title; const MobileProductView({Key? key, required this.title}) : super(key: key); @overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(title),),drawer: NavigationDrawer(),body: GridView.count(crossAxisCount: 1,children: DUMMY_DATA.map((product) =>Product(id: product.id, name: product.name, imageUrl: product.imageUrl, color: product.color, price: product.price)).toList(),));} }
上面的代码显示了应用程序使用来自 dummy_data.dart文件来建立一个列表 Product呈现为 GridView 的小部件。
渲染桌面屏幕
本节将包含应用程序桌面视图的实现。 创建一个名称为 Dart 的文件 desktop_product_view在里面 screens包裹。 将以下代码添加到文件中:
import 'package:flutter/material.dart'; import 'package:shop_app/widgets/product.dart'; import '../dummy_data.dart'; class DesktopProductView extends StatelessWidget {final String title;final int crossAxisCount; const DesktopProductView({Key? key, required this.title, required this.crossAxisCount}) : super(key: key);@overrideWidget build(BuildContext context) { Widget getAction(String actionName){return TextButton(onPressed: () {},child: Text(actionName,style: TextStyle(color: Theme.of(context).accentColor,fontWeight: FontWeight.w500),),);} return Scaffold(appBar: AppBar(title: Text(title),actions: [Padding(padding: const EdgeInsets.only(right: 100.0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [getAction('Home'),getAction('About'),getAction('Contact'),getAction('Order'),],),)],),body: Padding(padding: const EdgeInsets.all(18.0),child: GridView.count(crossAxisCount: crossAxisCount,children: DUMMY_DATA.map((product) =>Product(id: product.id,name: product.name,imageUrl: product.imageUrl,color: product.color,price: product.price)).toList(),),),);} }
从上面的代码中,小部件接收到 crossAxisCount值通过它的构造函数。 使用此值,它指定的数量 Product要在其中呈现的小部件 GridView在运行时。
中的动作 AppBar小部件将替换您为移动视图创建的抽屉。 使用 getAction方法,您为 AppBar.
实施 LayoutBuilder班级
的实施 LayoutBuilder课程将在 main.dart文件。 这是因为我们将应用程序的整个屏幕设计为自适应的。 因此, LayoutBuilder应该从小部件的最外层构建界面,在这种情况下,它被放置在 main.dart文件。
这是您应该添加到 main.dart文件:
import 'package:flutter/material.dart'; import 'package:shop_app/screens/mobile_product_view.dart'; import 'screens/desktop_product_view.dart'; void main() {runApp(const MyApp()); } class MyApp extends StatelessWidget {const MyApp({super.key}); @overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.purple,accentColor: Colors.amber),home: const MyHomePage(title: 'Flutter adaptive app'),);} } class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title; @overrideState<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @overrideWidget build(BuildContext context) {return LayoutBuilder(builder: (ctx, constraints){if(constraints.maxWidth < 600){return MobileProductView(title: widget.title);}int crossAxisCount = 0;if(constraints.maxWidth > 1400) {crossAxisCount = 4;} else if(constraints.maxWidth > 900) {crossAxisCount = 3;} else if (constraints.maxWidth >= 600) {crossAxisCount = 2;}return DesktopProductView(title: widget.title, crossAxisCount: crossAxisCount);});} }
这 build的方法 MyHomePage小部件使用 LayoutBuilder基于以下约束:
如果设备屏幕的宽度小于 600 像素,则构建 MobileProductView,它代表您在上一节中为移动设备创建的界面
如果设备屏幕的宽度大于 600 像素,则构建 DesktopProductView但与指定 crossAxisCount,取决于设备宽度,如代码所示
如果您打算只使应用程序的特定部分而不是如上所示的整个屏幕自适应,则必须实现 LayoutBuilder在小部件的所需部分。
结论
使您的应用程序适应不同的操作系统和设备屏幕尺寸是改善其用户体验的好方法。 本教程演示了如何使用 Flutter 使您的应用程序自适应 LayoutBuilder班级。 其他选项,例如 自适应布局包。 如文章开头所述,在构建自适应应用程序时,您还可以采用
本教程中构建的项目可在 GitHub 上找到。 我还建议您浏览 Flutter 文档,以更广泛地了解如何在应用程序的不同部分构建自适应布局。
使用 Flutter 创建自适应应用相关推荐
- Flutter Layout之创建自适应应用程序
概述 Flutter提供了从单一代码库构建可移动设备.桌面和Web上运行的应用程序的新机会.然而,伴随着这些机遇,新的挑战也随之而来.您希望您的应用程序让用户感到熟悉,通过最大限度地提高可用性并保存舒 ...
- 使用负边距创建自适应宽度的流体布局
随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题.目前国内门户很多都进行了改版,采用目前的主流–960px左右的宽度. 我认为 ...
- flutter创建/导入区块链钱包,获取余额
flutter创建/导入区块链钱包,获取余额 作为区块链行业小白刚接触区块链钱包几天,来开发钱包还是需要付出很多努力的.下面记录一下flutter框架下创建/导入区块链钱包,并获取余额 一.创建钱包: ...
- Flutter 创建第一个应用程序
转载https://www.tuicool.com/articles/vYf6raZ 主要介绍下如何用Flutter创建一个应用程序,程序一步步由简单到复杂,操作步骤来自官方,我觉得官方的已经写得挺好 ...
- flutter创建可移动的stack小部件
本文主要介绍我为桌面和 Web 设计的一个超级秘密 Flutter 项目使用了画布和可拖动节点界面.本教程将展示我如何使用堆栈来使用小部件完成可拖动功能 如下所示. 我们将动态地将项目添加到堆栈中并区 ...
- Flutter创建圆圈图标按钮
我找不到任何显示如何创建IconButton类似于的圆的示例FloatingActionButton.任何人都可以建议创建一个自定义按钮的方式/需要什么FloatingActionButton吗? 我 ...
- flutter创建一个demo
上一篇开始介绍: 安装flutter和 dart的plugin插件: 启动一个app的demo: 注意和创建Android应用一样: 目录: - projectName-android //andro ...
- 如何使用Flutter创建一个瀑布流布局的应用?
前言 本文着眼于搭建一个瀑布流布局的简化版Flutter应用,Joe大叔将从应用创建.插件引入.页面布局三个方面,给朋友们展示瀑布流的布局方式.既此,我们接着往下看.文末有GitHub项目地址 一.瀑 ...
- flutter创建新项目或者接手老项目配置环境依赖等全流程
1,下载android studio和flutter sdk(接收别人项目先看第9条,直接去官网下载正确的版本) 2,将android studio代理关闭: 打开as->File->Se ...
最新文章
- 两个月入门深度学习,全靠动手实践!一位前端小哥的经验分享
- kafka消费者如何读同一生产者消息_Kafka消费者生产者实例
- Springboot中的缓存Cache和CacheManager原理介绍
- 给 COLA 做减法:应用架构中的“弯弯绕设计”
- leetcode 289. Game of Life | 289. 生命游戏(Java)
- 文件服务器(虚拟目录)配置步骤
- listView当中有嵌套了有onClickListener的控件时ListView自身的onItemClick无响应的解决方案...
- Spring Boot注解详解
- MySQL安装过程中出现的vcruntime140.dll缺失
- 学习python_day2
- SVM入门(一)至(三)(转)
- 排序的概念及分类实现
- MAVEN本地仓库安装及配置
- web和mysql连接并增删改查_Web界面实现数据库增删改查过程
- kafka生产者发送消息提升效率策略设置
- 【ESP32-IDF】02-2 外设-触摸传感器
- 什么是“懒加载”(Lazy Loading)?
- 文科女生转行软件测试之路
- 一个nginx部署多个项目
- python自动拼图_Python图像处理——人物拼图游戏