[译] 深入了解 Flutter

使用 Flutter 的 UI-as-code 方法时,组件树就是代码。因此,我们可以使用所有常用的代码组织工具来改善这种情况。工具箱中最简单的工具之一就是命名子表达式。这会在语法上将组件树翻出来。而不是

return A(B(C(D(), E())), F());

我们可以命名每个子表达式并得到

final Widget d = D();
final Widget e = E();
final Widget c = C(d, e);
final Widget b = B©;
final Widget f = F();
return A(b, f);

我们的湖泊应用可以重写成下面这样:

import ‘package:flutter/material.dart’;

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Widget imageSection = Image.asset(
‘images/lake.jpg’,
width: 600.0,
height: 240.0,
fit: BoxFit.cover,
);
final Widget titles = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
‘Oeschinen Lake Campground’,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Text(
‘Kandersteg, Switzerland’,
style: TextStyle(color: Colors.grey[500]),
),
],
);
final Widget stars = Row(
children: [
Icon(Icons.star, color: Colors.red[500]),
Text(‘41’),
],
);
final Widget titleSection = Container(
padding: const EdgeInsets.all(32.0),
child: Row(
children: [
Expanded(child: titles),
stars,
],
),
);
final Widget callAction = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.call, color: Theme.of(context).primaryColor),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
‘CALL’,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryColor,
),
),
),
],
);
final Widget routeAction = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.near_me, color: Theme.of(context).primaryColor),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
‘ROUTE’,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryColor,
),
),
),
],
);
final Widget shareAction = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.share, color: Theme.of(context).primaryColor),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
‘SHARE’,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryColor,
),
),
),
],
);
final Widget actionSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
callAction,
routeAction,
shareAction,
],
),
);
final Widget textSection = Container(
padding: const EdgeInsets.all(32.0),
child: Text(
'Lake Oeschinen lies at the foot of the Blüemlisalp in the ’
'Bernese Alps. Situated 1,578 meters above sea level, it ’
'is one of the larger Alpine Lakes. A gondola ride from ’
'Kandersteg, followed by a half-hour walk through pastures ’
'and pine forest, leads you to the lake, which warms to ’
'20 degrees Celsius in the summer. Activities enjoyed here ’
‘include rowing, and riding the summer toboggan run.’,
softWrap: true,
),
);
final Widget scaffold = Scaffold(
appBar: AppBar(title: Text(‘Top Lakes’)),
body: ListView(
children: [
imageSection,
titleSection,
actionSection,
textSection,
],
),
);
return MaterialApp(
title: ‘Flutter Demo’,
home: scaffold,
);
}
}

缩进级别现在更合理,我们可以通过引入更多名称使子树的缩进级别变得像我们希望的那样浅。更好的是,通过为各个子树提供有意义的名称,我们可以表示每个子树的作用。所以我们现在可以谈谈 xxxAction 子树…并观察到我们在这里面有很多重复的代码!另一个基本的代码组织工具——功能抽象——负责这部分内容:

import ‘package:flutter/material.dart’;

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Widget imageSection = …
final Widget titles = …
final Widget stars = …
final Widget titleSection = …

Widget action(String label, IconData icon) {
final Color color = Theme.of(context).primaryColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
label,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}

final Widget actionSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
action(‘CALL’, Icons.call),
action(‘ROUTE’, Icons.near_me),
action(‘SHARE’, Icons.share),
],
),
);
final Widget textSection = …
final Widget scaffold = …
return MaterialApp(
title: ‘Flutter Demo’,
home: scaffold,
);
}
}

我们将看到一个简单功能抽象的替代,它会更具有更 Flutter 风格的。

重新审视组成

接下来是什么?好吧,build 方法依然很长。也许我们可以提取一些有意义的作品…片断?组件!Flutter 的组件都是关于组合和重用的。我们用框架提供的简单组件组成了一个复杂的组件 但是发现结果过于复杂,我们可以选择把它分解成不太复杂的自定义组件。定制组件是 Flutter 世界中的一等公民,而明确定义的组件具有很大的潜力被重用。让我们将 action 函数转换为 Action 组件类型并将其放在自己的文件中:

import ‘package:flutter/material.dart’;
import ‘src/widgets.dart’;

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Widget imageSection = …
final Widget titles = …
final Widget titleSection = …
final Widget actionSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Action(label: ‘CALL’, icon: Icons.call),
Action(label: ‘ROUTE’, icon: Icons.near_me),
Action(label: ‘SHARE’, icon: Icons.share),
],
),
);
final Widget textSection = …
final Widget scaffold = …
return MaterialApp(
title: ‘Flutter Demo’,
home: scaffold,
);
}
}

import ‘package:flutter/material.dart’;

class Action extends StatelessWidget {
Action({Key key, this.label, this.icon}) : super(key: key);

final String label;
final IconData icon;

@override
Widget build(BuildContext context) {
final Color color = Theme.of(context).primaryColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
label,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}
}

现在我们可以在应用程序的任何位置重用 Action 组件,就像它是由 Flutter 框架定义的一样。

但是,嘿,顶级的 action 功能不能满足同样的需求吗?

一般来说,不能。

  • 许多组件是由其他组件构造的;它们的构造函数有 WidgetList<Widget> 类型的 childchildren 参数。所以 action 函数不能传递给任何一个函数。当然,调用 action 的结果可以。但是,你将通过在当前构建环境中预先构造的组件树,而不是 StatelessWidget,它只在必要时才构建子树,并且是最后在整个树中定义的上下文中定义的。注意到表达式中在 Action.build 开头的 Theme.of(context).primaryColor 了吗?它从父链上最近的 Theme 组件中检索主颜色——在调用 action 时,它很可能与最近的 Theme 不同。
  • Action is defined as a StatelessWidget which is little more than a build function turned into an instance method. But there are other kinds of widget with more elaborate behavior. Clients of Action shouldn’t care what kind of widget Action is. As an example, if we wanted to endow Action with an intrinsic animation, we might have to turn it into a StatefulWidget to manage the animation state. The rest of the app should be unaffected by such a change.

重新审视响应式编程

状态管理是开始利用 Flutter 响应式编程模型,并让我们的静态视图生动起来的暗示。让我们定义应用程序的状态。我们将尽量保持简单,先假设一个 Lake 业务逻辑类,其唯一可变状态是用户是否已加星标:

abstract class Lake {
String get imageAsset;
String get name;
String get locationName;
String get description;

int get starCount;
bool get isStarred;
void toggleStarring();

void call();
void route();
void share();
}

然后,我们可以从 Lake 实例动态地构造我们的组件树,并且同时还可以设置事件处理程序以调用其方法。响应式编程模型的优点在于我们只需在代码库中执行一次。只要 Lake 实例发生变化,Flutter 框架就会重建我们的组件树——前提是我们告诉框架。这需要使 MyApp 成为一个 StatefulWidget,这反过来又涉及将组件构建委托给一个相关的 State 对象,然后每当我们在 Lake 上加星标时调用 State.setState 方法。

import ‘package:flutter/material.dart’;
import ‘src/lake.dart’;
import ‘src/widgets.dart’;

void main() {
// 假装我们从业务逻辑中获取 Lake 实例。
final Lake lake = Lake();
runApp(MyApp(lake));
}

class MyApp extends StatefulWidget {
final Lake lake;

MyApp(this.lake);

@override
MyAppState createState() => new MyAppState();
}

class MyAppState extends State {
@override
Widget build(BuildContext context) {
final Lake lake = widget.lake;
final Widget imageSection = Image.asset(
lake.imageAsset,
width: 600.0,
height: 240.0,
fit: BoxFit.cover,
);
final Widget titles = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
lake.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Text(
lake.locationName,
style: TextStyle(color: Colors.grey[500]),
),
],
);
final Widget stars = GestureDetector(
child: Row(
children: [
Icon(
lake.isStarred ? Icons.star : Icons.star_border,
color: Colors.red[500],
),
Text(‘${lake.starCount}’),
],
),
onTap: () {
setState(() {
lake.toggleStarring();
});
},
);
final Widget titleSection = Container(
padding: const EdgeInsets.all(32.0),
child: Row(
children: [
Expanded(child: titles),
stars,
],
),
);
final Widget actionSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Action(label: ‘CALL’, icon: Icons.call, handler: lake.call),
Action(label: ‘ROUTE’, icon: Icons.near_me, handler: lake.route),
Action(label: ‘SHARE’, icon: Icons.share, handler: lake.share),
],
),
);
final Widget textSection = Container(
padding: const EdgeInsets.all(32.0),
child: Text(
lake.description,
softWrap: true,
),
);
final Widget scaffold = Scaffold(
appBar: AppBar(title: Text(‘Top Lakes’)),
body: ListView(
children: [
imageSection,
titleSection,
actionSection,
textSection,
],
),
);
return MaterialApp(
title: ‘Flutter Demo’,
home: scaffold,
);
}
}

import ‘package:flutter/material.dart’;

class Action extends StatelessWidget {
Action({Key key, this.label, this.icon, this.handler}) : super(key: key);

final String label;
final IconData icon;
final VoidCallback handler;

@override
Widget build(BuildContext context) {
final Color color = Theme.of(context).primaryColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(icon, color: color),
onPressed: handler,
),
Text(
label,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
],
);
}
}

这有用,但效率不高。最初的挑战是深度嵌套的组件树。那个树仍然在那里,如果不在我们的代码中,那么就在运行时。重建所有这些只是为了切换切换星标完全是一种浪费。当然,Dart 的实现可以非常有效地处理短寿命对象,但如果你反复重建,Dart 也会耗尽你的电池——特别是涉及动画的地方。一般来说,我们应该将重建限制在实际改变的子树上。

你有没有抓住这个矛盾?组件树是用户界面的不可变描述。如何在不从根重构的情况下重建其中的一部分?实际上,组件树不是具有从父组件到子组件,从根到叶的引用的物化树结构。特别是 StatelessWidgetStatefulWidget,它们没有子引用。他们提供的是 build 方法(在有状态的情况下,通过相关的 State 实例)。Flutter 框架递归地调用那些 build 方法,同时生成或更新实际的运行时树结构,不是组件,而是引用组件的 Element 实例。元素树是可变的,并由 Flutter 框架管理。

那么当你在 State 实例 s 上调用 setState 时会发生什么?Flutter 框架标记了以 s 对应元素为根的子树,用于重建。当下一帧到期时,该子树将根据 sbuild 方法返回的组件树进行更新,而后者依赖于当前的应用程序状态。

我们对代码的最终尝试提取了一个有状态的 LakeStars 组件,将重建限制在一个非常小的子树中。而 MyApp又变回无状态。

import ‘package:flutter/material.dart’;
import ‘src/lake.dart’;
import ‘src/widgets.dart’;

void main() {
// 假装我们从业务逻辑中获取 Lake 实例。
final Lake lake = Lake();
runApp(MyApp(lake));
}

class MyApp extends StatelessWidget {
const MyApp(this.lake);

final Lake lake;

@override
Widget build(BuildContext context) {
final Widget imageSection = …
final Widget titles = …
final Widget titleSection = Container(
padding: const EdgeInsets.all(32.0),
child: Row(
children: [
Expanded(child: titles),
LakeStars(lake: lake),
],
),
);
final Widget actionSection = …
final Widget textSection = …
final Widget scaffold = …
return MaterialApp(
title: ‘Flutter Demo’,
home: scaffold,
);
}
}

import ‘package:flutter/material.dart’;
import ‘lake.dart’;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
长且无助**。

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-AtLKgZ4G-1715791643379)]

[外链图片转存中…(img-SM0hHnq8-1715791643382)]

[外链图片转存中…(img-Rv4qC1CV-1715791643383)]

[外链图片转存中…(img-fMZp11OK-1715791643384)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flutter 官网网址为 https://flutter.dev/ 。Flutter 是 Google 推出的一种用于构建跨平台应用程序的开源框架。它可以用于构建移动应用程序、Web 应用程序和桌面应用程序。 ### 回答2: Flutter 官网是由谷歌开发和维护的一个网站,旨在提供关于Flutter跨平台应用程序开发框架的信息和资源。 访问Flutter官网,可以了解最新的Flutter开发工具、文档、教程和示例代码。官网上提供了详细的安装步骤和配置指南,以帮助开发者快速入门Flutter开发。同时,官网还提供了丰富的文档资料,包括Flutter框架、Widgets、API等的详细说明,方便开发者深入了解和学习。 在Flutter官网上,开发者可以找到各种类型的示例代码,包括基础示例、布局示例和动画示例等,这些示例代码可以帮助开发者快速上手并实现自己的应用程序。此外,官网还提供了Flutter的插件和工具列表,开发者可以根据自己的需求选择合适的插件和工具来提高开发效率。 Flutter官网还有一个活跃的社区,开发者可以在社区中与其他Flutter开发者交流经验、提问问题和分享开发成果。在官网上,开发者可以找到Flutter的最新动态、版本发布和更新信息,及时了解Flutter的最新进展。 总而言之,Flutter官网是一个全面的资源平台,为开发者提供了丰富的开发工具、文档和示例代码,帮助开发者快速学习和使用Flutter开发跨平台应用程序。 ### 回答3: Flutter 官网是谷歌推出的一种用于构建跨平台移动应用的框架。它提供了一种快速、高效、美观的方式来开发iOS和Android应用程序。Flutter 官网是开发者们获取有关Flutter相关信息的主要来源。 在Flutter 官网上,开发者可以找到包括入门指南、教程、示例代码、文档和API参考等在内的丰富资源。这些资源不仅帮助开发者快速入门,还提供了各种使用Flutter构建应用所需的实用知识和技巧。 Flutter 官网还提供了一个专门的社区论坛,开发者可以在这里与其他Flutter开发者交流经验、提问问题、分享开发心得和解决方案。这个社区论坛是一个包容和友好的环境,鼓励开发者们互相学习和帮助。 另外,Flutter 官网还定期举办在线研讨会和Flutter相关的活动,为开发者们提供一个学习和交流的平台。开发者们可以通过这些活动进一步了解最新的Flutter开发技术和最佳实践。 总的来说,Flutter 官网是一个开发者们获取有关Flutter最新信息、技术文档和社区交流的综合平台。它为开发者们提供了一个快速入门和深入学习Flutter的渠道,有助于开发者们更好地应用和掌握这一跨平台移动应用开发框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值