我如何在Flutter上做桌面应用程序(+奖金)

最近,有个新闻引起了我的注意,即Flutter(1.9)的下一版本已发布 ,它具有许多优点,包括对Web应用程序的早期支持。

在工作中,我正在React React上开发移动应用程序,但出于好奇,我对Flutter颇有兴趣。 对于那些尚不了解的人:在Flutter上,您已经可以创建Android和iOS的应用程序,正在准备发布Web应用程序,还计划支持桌面。

这就是“一环统治一切”。

经过几天的思考之后,我决定尝试使用哪种应用程序,我决定选择一个带有星号的任务-这些破旧的轨道需要什么? 在桌面上摇摆并英勇地克服了困难! 展望未来,我会说几乎没有困难。

切入-关于我如何使用Flutter工具解决通常的React Native程序员任务的故事,以及该技术的总体印象。



考虑到我想“触摸” Flutter的哪些功能,我决定在我的应用程序中应该是:

  • 向远程API的请求;
  • 屏幕之间的过渡;
  • 过渡动画
  • 状态管理员-redux或类似的东西。

我不知道如何后端,所以我决定寻找第三方开放API。 结果,我选择了此资源-XML和JSON,API的CBR课程 。 好了,我终于决定了该应用程序的功能:将有两个屏幕,主要是一个屏幕,以CBR汇率列出货币,当您单击一个列表项时,我们将打开一个包含详细信息的屏幕。

准备工作


由于flutter create命令尚不知道如何为Windows / Linux创建项目(当前仅支持Mac,为此请使用--macos标志),因此您必须使用存储库,其中有一个准备好的示例。 我们克隆存储库,从那里获取example文件夹,如有必要,将其重命名并继续在其中工作。

由于仍在开发对桌面平台的支持,因此您仍然需要执行许多操作。 要访问正在开发的功能,请在终端中运行:

 flutter channel master flutter upgrade 

另外,您需要告诉Flutter它可以使用您的平台:

 flutter config --enable-linux-desktop 



 flutter config --enable-macos-desktop 



 flutter config --enable-windows-desktop 

如果一切顺利,那么通过运行flutter doctor命令,您应该会看到类似的输出:



所以,风景已经准备好了,大厅里的观众们-我们可以开始了。

布局图


React Native之后引起您注意的第一件事是缺少一种特殊的标记语言la JSX。 Flutter迫使您在Dart中编写标记和业务逻辑。 最初,这很烦人:外观没什么变化,代码看起来很笨拙,甚至这些括号都位于组件的末尾!

例如:



这不是极限! 值得在错误的地方删除一个,并保证为您带来愉快的(没有)消遣。

另外,由于Flutter中样式组件的特殊性,对于大型组件,从编辑器左边缘开始的缩进非常迅速地增加,并且括号的数量也随之关闭。

此功能是在Flutter中,样式是相同的组件(更确切地说是小部件)。

如果在React Native中在View连续排列三个按钮,以便它们均匀地分布容器空间,那么对于我来说,为样式中的View指定flexDirection: 'row'为样式中的按钮添加flex: 1足够了,那么Flutter具有一个单独的组件一行,用于在一个行中排列元素,而另一个行用于在整个可用空间中元素的“可扩展性”: Expanded

结果,代替

 <View style={{height: 100, width:300, flexDirection: 'row'}}> <Button title='A' style={{flex:1}}> <Button title='B' style={{flex:1}}> <Button title='C' style={{flex:1}}> </View> 

我们必须这样写:

 Container( height: 100, width: 300, child: Row( children: <Widget>[ Expanded( child: RaisedButton( onPressed: () {}, child: Text('A'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('B'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('C'), ), ), ], ), ) 

比较冗长,不是吗?

或者说,您想向此容器添加带有圆角边缘的框架。 在React Native中,我们只需添加以下样式:

 borderRadius: 5, borderWidth: 1, borderColor: '#ccc' 

在Flutter中,我们必须在容器参数中添加以下内容:

 decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(width: 1, color: Color(0xffcccccc)) ), 

总的来说,起初,我的标记变成了大量的代码,其中的恶魔会折断他的腿。 但是,并非一切都那么糟糕。

首先,当然必须分解大型组件-将其放置在单独的小部件中,或者至少放置在小部件类的方法中。

其次,VS Code中的Flutter插件有很大帮助-在上图中,括号中的注释由插件本身签名(并且它们不可删除),这有助于避免括号引起混淆。 加上自动格式化工具-半小时后,您就会习惯于定期按Ctrl+Shift+I来格式化代码。

此外,第二版中Dart语言的语法变得更加令人愉悦,因此到今天结束,我已经很喜欢使用它了。 不寻常? 是的 但不是不愉快的。

API请求


在React Native中,为了从某些API获取数据,我们通常使用fetch方法,该方法将Promise返回Promise我们。

在Flutter中,情况类似。 在查看了文档中的示例之后,我将http包添加到pubspec.yaml (来自JS world的package.json的类似物),并编写了如下内容:

 Future<http.Response> getAnything() { return http.get(URL); } 

Future对象的含义与Promise非常相似,因此此处的所有内容都非常透明。 好吧,对于序列化/反序列化json对象,您可以将模型类的概念与特殊方法fromJSON / toJSON 。 您可以在文档中阅读有关此内容的更多信息。

屏幕之间的过渡


尽管我正在制作桌面应用程序,但从Flutter的角度来看,它在哪个平台上旋转都没有区别。 好吧,也就是说,就我而言,通常是这样-我不知道。 实际上,启动颤动应用程序的系统窗口与智能手机的屏幕相同。

屏幕之间的转换非常简单:我们创建一个屏幕小部件类,然后使用标准的Navigator类。

在最简单的情况下,它可能看起来像这样:

 RaisedButton( child: Text('Go to Detail'), onPressed: () { Navigator.of(context).push<void>(MaterialPageRoute(builder: (context) => DetailScreen())); }, ) 

如果您的应用程序有多个屏幕,则首先准备一个路由字典,然后使用pushNamed方法更为合理。 文档中的一个小例子:

 class NavigationApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( ... routes: <String, WidgetBuilder>{ '/a': (BuildContext context) => usualNavscreen(), '/b': (BuildContext context) => drawerNavscreen(), } ... ); } } // AnyWidget ... onPressed: () { Navigator.of(context).pushNamed('/a'); }, ... 

另外,您可以准备一个特殊的动画以在屏幕之间切换并编写如下内容:

 Navigator.of(context).push<void>(ScaleRoute(page: DetailScreen())); 

ScaleRoute是用于创建过渡动画的特殊类。 这样的动画的很好的例子可以在这里找到。

国家管理


碰巧我们需要从应用程序的任何部分访问某些数据。 在React Native中, redux通常用于这些目的(如果不是最常见的话)。

对于Flutter,有一个存储库 ,显示了使用各种应用程序架构的示例-Redux,MVC和MVU,甚至是我以前从未听说过的那些。

在这些示例中稍作改动后,我决定停止使用Provider

总的来说,这个想法很简单:我们创建一个ChangeNotifier类的特殊类,在其中存储数据,使用此类的方法对其进行更新,并在必要时从那里进行提取。 有关更多详细信息,请参见软件包文档

为此,请将provider包添加到pubspec.yaml并准备Provider类。 就我而言,它看起来像这样:

 import 'package:flutter/material.dart'; import 'package:rates_app/models/rate.dart'; class RateProvider extends ChangeNotifier { Rate currentrate; void setCurrentRate(Rate rate) { this.currentrate = rate; notifyListeners(); } } 

这里Rate是我的货币模型类(带有namecodevalue等字段), currentrate是将存储所选货币的字段, setCurrentRatecurrentrate值的方法。

为了将我们的提供程序附加到应用程序,我们更改了应用程序类代码:

 @override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (context) => RateProvider(), //   child: MaterialApp( ... ), home: HomeScreen(), ), ); } 

就是这样,现在,如果我们要保存所选的货币,则可以编写如下代码:

 Provider.of<RateProvider>(context).setCurrentRate(rate); 

如果我们想获取存储的值,则可以这样:

 var rate = Provider.of<RateProvider>(context).currentrate; 

一切都很透明,没有样板(与Redux不同)。 当然,对于更复杂的应用程序,可能一切都不会那么顺利,但是对于像我的例子那样的纯葡萄酒。

构建应用


从理论上讲, flutter build <platform>命令用于构建应用程序。 实际上,当我执行flutter build linux命令时,我收到以下消息:



“它没受伤。”我想, build文件夹的重量让我感到震惊-287.5 MB-由于我的灵魂很简单,我永远删除了该文件夹。 事实证明-是徒劳的。

删除build目录后,项目停止启动。 我无法还原它,所以我从原始示例中复制了它。 它没有帮助-收集器发誓丢失文件。

经过一番研究,结果发现此文件夹中有一个snapshot_blob.bin.d文件,显然,其中写入了项目中使用的所有文件的路径。 我添加了缺少的路径,并且有效。

因此,Flutter目前不知道如何为桌面准备发行版本。 无论如何,对于Linux。

总的来说,如果您不注意这一点,该应用程序就会变成我想要的样子

所以


红利


我们传递给承诺的奖金。

即使在编写应用程序的阶段,我仍然希望检查将其移植到其他平台有多困难。 让我们从手机开始。

当然,有一种不太野蛮的方法,但是我决定最短的路径是直接的。 因此,我只是创建了一个新的Flutter项目,将pubspec.yaml文件, assetsfontslib目录转移到其中,并将该行添加到AndroidManifest.xml

 <uses-permission android:name="android.permission.INTERNET" /> 

该应用程序以半踢开始,我知道了

图片


一开始,我不得不修改网络。 我不知道如何创建Web项目,因此我使用了Internet上的说明,由于某些原因,该说明不起作用。 我已经想吐了,但是碰到了这本手册。

结果,一切都变得尽可能简单-所需要的只是启用对Web应用程序的支持。 挤压手册:

 flutter channel master flutter upgrade flutter config --enable-web cd <into project directory> flutter create . flutter run -d chrome 

然后,我以同样的野蛮方式将必要的文件传输到该项目,并收到

结果


总体印象


最初,与Flutter合作并不寻常,我一直试图使用React Native的常规方法,但这样做会造成干扰。 另外,dart代码的一些冗余也很烦人。

当我握了一下手(和锥体)后,我开始看到Flutter比React Native的优势。 我会列出一些。

语言 。 Dart是完全可理解且不错的语言,具有强大的静态类型。 使用JavaScript后,就像呼吸了一口新鲜空气。 我不再担心我的代码会在运行时中断,这是一种令人愉快的感觉。 有人可能会说有Flow和TypeScript,但事实并非如此-在我们的项目中,我们同时使用了两者,而其他东西总是在某个地方出现问题。 当我用React Native编写代码时,我不禁觉得自己的代码在火柴棍道具上,随时可能中断。 使用Flutter,我忘了这种感觉,如果价格是代码冗余,那么我准备付款。

平台 。 在React Native中,您使用本机组件,这通常很好。 但是正因为如此,您有时必须编写特定于平台的代码,并捕获特定于每个平台的错误。 它可能令人难以置信。 使用Flutter,您可以像噩梦一样忘记这些问题(尽管在大型应用程序中情况可能并不那么顺利)。

环境 。 在React Native中使用环境时,一切都是令人悲伤的。 vscode插件会不断脱落,调试器可以吞噬16个可操作的组件和70个可互换的组件,紧密挂起系统(根据个人经验),以及最常见的错误纠正方案:“删除node_modules,再次安装软件包,然后尝试重新启动几次。” 这通常会有所帮助,但是太过分了! 事实并非如此,并非如此。

此外,您将必须定期运行AndroidStudio和Xcode,因为其中的某些功能仅通过这种方式放置(公平地说,随着RN 0.60的发布,情况会变得更好)。

在这种背景下,vscode的官方Flutter插件看起来非常不错。 代码提示使您无需了解文档即可熟悉平台,自动格式化可解决编码风格,常规调试器等问题。
通常,它看起来像是一种更成熟的工具。

跨平台 。 React Native奉行“学习一次,到处编写”的原则-学习后,您可以为不同的平台编写代码。 的确,在每个平台下,您都会遇到特定的问题。 但这也许仅仅是React Native不成熟的结果-目前,最新的稳定版本是0.61。 也许随着1.0版的发布,这些问题中的大多数都会消失。

Flutter方法更像一次编写,可在任何地方编译。 即使目前还不能准备生产台式机,Web也处于Alpha状态,但一切都已实现。 对于所有平台拥有单一代码库的能力是一个很强的理由。

当然,Flutter也不是没有缺陷,但是使用它的一些经验不能使我识别出它们。 因此,如果您要进行更客观的评估-请随时轻视新奇效果。

总的来说,应该注意的是,尽管他有成长的空间,但Flutter大多留下了积极的感觉。 在下一个项目中,我将更愿意开始而不是在React Native上开始。

该项目的源代码可以在GitHub找到

附言:我借此机会祝贺过去的教师节活动中的所有参加者。

Source: https://habr.com/ru/post/zh-CN470251/


All Articles