扑 利弊



首先,让我们谈谈Flutter是什么。 这是用于从Google创建移动应用程序的框架。 它是跨平台的,允许您为3个操作系统编译创建的项目:
  • 安卓系统
  • 的iOS
  • 紫红色

此外,对于最新的操作系统-Fuchsia-这是创建应用程序的唯一方法。
自2015年以来,Flutter长期以来仅以alpha和beta版本提供。 第一个稳定版本于2018年12月4日发布。


Google积极推动Flutter的发展,并逐渐获得欢迎,并且很可能会继续淘汰当前使用的其他跨平台开发工具(React Native,Xamarin),尤其是在樱红色广泛分布的情况下。 鉴于Google将该操作系统定位为Android的替代产品,Flutter迟早将取代本地Android开发。 因此,展望和积极发展是Flutter的主要优势。


+展望与积极发展


让我们看看它是如何工作的。


使用Dart编程语言,将创建一个移动应用程序,其中包含图形界面的描述以及整个工作逻辑。 工作结果将添加到本机应用程序中,例如图片,字体等(当然,此过程是自动化的)。


同时,在应用程序的本机部分中,创建了一个屏幕,在该屏幕中加载了运行Flutter的Dart虚拟机。


请注意,Flutter的缺点之一来自于此:


-随着将Dart虚拟机添加到其中,最终安装包会更大。


因此,存在Flutter文件,并且根据编译的类型(iOS或Android)添加了虚拟机。


虚拟机具有自己的图形引擎,它使用屏幕,对话框,片段等之间的所有转换来绘制应用程序界面。 在这种情况下,Flutter下的开发与使用真正的Android和iOS组件的Xamarin和React Native进行的开发明显不同。 对于它们,不可能使用特定于平台的组件(如果有此需要,则必须创建两个版本的UI)。 使用Flutter选择设计时,只需专注于一个平台(例如Android)即可。 在为iOS构建项目时,您会看到一个标准的Android界面。 看起来有些奇怪和出乎意料,但是很有用(以后可以改进界面)。


+自己的图形引擎(无需为Android和iOS单独创建界面)


现在关于印象。


在将多个应用程序从Android移植到Flutter时,我们注意到了一些差异,这些差异可以视为正负。


首先引起您注意的是创建屏幕的方法,该方法与Android和iOS上使用的显着不同。 在Android中,逻辑和接口是分开的:逻辑由代码设置,接口由xml中的布局设置。 在Flutter上,全部使用代码进行设置。 尽管此处使用了一种特殊的界面样式-创建的界面元素彼此嵌套。 这有点像布局,在React Native中工作的方式非常相似。 但是,不可能直接访问这些元素。 要更改屏幕上的某些内容,您必须更新整个屏幕,或者使用在创建过程中预先添加到窗口小部件的特殊控制器。


-接口是使用代码创建的,这使得逻辑和设计之间的界限变得更细。


另一方面,这种方法可以更轻松地将屏幕拆分为单独的组件。 实际上,任何嵌套接口元素块都可以通过几个步骤移动到单独的小部件中,这比创建自定义View和片段要容易得多。


+接口很容易分成单独的模块


最后两句话可能值得研究。 为此,我们将编写一个简单的应用程序,演示Flutter的某些功能。 这将是具有标准导航栏和标签栏的应用程序。


让我们制作三个标签:


1)首先-使用文字和滑块调整文字的大小和颜色
2)将可下载的图像添加到第二个图像(带有进度指示器)
3)第三名示例列表




还要假设我们最初并未将应用程序界面拆分为单独的选项卡。 在Flutter中,您可以在不使用片段的情况下实现此屏幕。 当然,仍然需要这样的分区,但是假设他们忘记这样做了,或者设计发生了变化,此后,由于惯性而继续进行了一级开发。

现在考虑实现此布局的源代码:


void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } 

该代码段几乎是所有Flutter应用程序的标准代码(它与项目一起创建)。


MyApp是应用程序本身的类,它描述创建MaterialApp时的常规参数:应用程序名称,字体,颜色和样式。 该应用程序的主屏幕也在此处显示(对于我们来说,这是MyHomePage)。


让我们做一个重要的说明:在Flutter中,小部件分为两种类型:


1)StatefulWidget
2)StatelessWidget


需要两个类来描述StatefulWidget:小部件本身的类及其状态的类(将在其中进行主要工作)。


StatelessWidget由具有固定状态的一个类描述,并且只能通过从主窗口小部件重新创建来更改。 因此,出于我们的目的,需要StatefulWidget。


现在考虑_MyHomePageState:


 class _MyHomePageState extends State<MyHomePage> { int _currentIndex = 0; double _size = 14; double _r = 0; double _g = 0; double _b = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: <Widget>[ 

为了便于理解,带有文本的选项卡用红色标记,带有图片的选项卡用绿色标记,列表选项卡用蓝色标记,导航菜单用黄色标记。 如您所见,该接口被描述为彼此嵌入的一组小部件(及其数组):










使用的功能:
  void _onTapped(int index) { setState(() { _currentIndex = index; }); } void _setTextStyle( {double size = -1, double r = -1, double g = -1, double b = -1}) { setState(() { if (size > 0) { _size = size; } if (r > 0) { _r = r; } if (g > 0) { _g = g; } if (b > 0) { _b = b; } }); } } 

让我们更详细地考虑它们:


onTapped-切换底部菜单中的选项卡时调用的函数。 它调用特殊的setState函数,该函数允许您使用新数据更新当前窗口小部件(并且我们更新了_currentIndex变量)。


让我们看看它的适用范围:


 body: <Widget>[    ][_currentIndex] 

在这里,我们处理一个数组,从中使用_currentIndex选择一个屏幕布局选项,并将其替换为选项卡之一。


接下来是_setTextStyle函数。 对于类似C的语言,它的广告非常不寻常。


 void _setTextStyle({double size = -1, double r = -1, double g = -1,double b = -1}) 

参数不是作为列表而是作为数组传递给它的。 这是Dart非常有趣的功能之一,与大多数其他语言相比,它允许您创建具有可变数量的参数的函数。


由于每个参数都是命名的,因此我们可以采用随机顺序。 例如:


 _setTextStyle(size: 24, b: 255) 

让我们将大屏幕类分解为小部件。 最好按逻辑元素细分,在我们的例子中,这些是制表符。 感谢Flutter的功能,为此,我们足以获取负责每个选项卡的代码片段,并将它们与逻辑一起使用build方法转移到单独的类中。


第一个标签:


 class TextWidget extends StatefulWidget { @override _TextWidgetState createState() => _TextWidgetState(); } class _TextWidgetState extends State<TextWidget> { double _size = 14; double _r = 0; double _g = 0; double _b = 0; @override Widget build(BuildContext context) { return Column( children: <Widget>[ Text("Example String", style: TextStyle( fontSize: _size, color: Color.fromRGBO(_r.toInt(), _g.toInt(), _b.toInt(), 1))), Container(constraints: BoxConstraints.expand(height: 32.0)), Slider( label: "${_size.toInt()} sp", value: _size, min: 10, max: 48, divisions: 38, activeColor: Colors.black, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(size: val)), Slider( label: _r.toInt().toString(), value: _r, min: 0, max: 255, divisions: 255, activeColor: Colors.red, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(r: val), ), Slider( label: _g.toInt().toString(), value: _g, min: 0, max: 255, divisions: 255, activeColor: Colors.green, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(g: val), ), Slider( label: _b.toInt().toString(), value: _b, min: 0, max: 255, divisions: 256, activeColor: Colors.blue, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(b: val), ), ], ); } } 

由于窗口小部件需要更新(_setTextStyle方法),因此我们使用StatefulWidget。
接下来的两个选项卡无需更新,因此我们将使用StatelessWidget。


第二个标签:


 class ImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Stack( children: <Widget>[ Center(child: CircularProgressIndicator()), Center( child: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: 'https://picsum.photos/250?image=9', ), ), ], ); } } 

第三个标签:


 class ListWidget extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( itemCount: 25, itemBuilder: (BuildContext context, int index) { return Container( child: Text( 'entry $index', style: TextStyle(color: Colors.white), ), margin: EdgeInsets.all(16.0), padding: EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all( Radius.circular(16.0), ), ), ); }, ); } } 

更改了主屏幕状态代码:


 class _MyHomePageState extends State<MyHomePage> { int _currentIndex = 0; Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), actions: <Widget>[ IconButton(icon: Icon(Icons.navigate_next), onPressed: next) ], ), body: <Widget>[ TextWidget(), ImageWidget(), ListWidget(), ][_currentIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: _onTapped, items: [ BottomNavigationBarItem( icon: new Icon(Icons.text_format), title: new Text('Text'), ), BottomNavigationBarItem( icon: new Icon(Icons.image), title: new Text('Image'), ), BottomNavigationBarItem( icon: Icon(Icons.list), title: Text('ListView'), ) ], )); } 

因此,我们可以轻松地将一个大屏幕拆分为一个小屏幕和三个小窗口部件。 您会注意到,在Flutter屏幕上,概念上与小部件没有什么不同(更确切地说,小部件承担着活动,片段和自定义视图的功能)。 当应用程序需要任何元素的全屏视图时,此功能非常方便-为此,您可以以最小的改进使用我们的小部件。


但是,用作屏幕的窗口小部件和常规窗口小部件之间的差异很小。 屏幕小部件的根元素应为Scaffold对象(允许您添加appBar,bottomNavigationBar,floatingActionButton,抽屉等)。


常规窗口小部件没有此限制,因为使用构建方法,它们将被嵌入在已有Scaffold的主屏幕中。


幸运的是,将Scaffold添加到常规窗口小部件不会影响其性能。


您还可以添加SafeArea(为状态栏提供缩进)。 获得以下简单转换:


来自:


 @override Widget build(BuildContext context) { return []; } 

至:


 @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: [] ), ); } 

好了,现在回到Flutter利弊的讨论。


Flutter最近已发布,因此错误很常见。 在更新Flutter时,这一点尤其明显-一些库开始出现错误。


-不稳定(最近才发布Beta版)


显然,使用新框架时,与本地Android / iOS开发相比,您拥有的库要少得多。 但是,Flutter仍然有很多库,并且它们继续以很高的速度出现。 例如,显然是在2018年下半年添加了许多库,以准备发布第一个稳定版本,而在此之前存在最重要的库(Google Analytics,Firebase,Maps等)。


-比本地开发更少的库
+最重要的库已经存在,新库不断涌现


现在该盘点一下! 让我们回顾一下所有的利弊,从最重要的优点到最重要的缺点进行排列:


+跨平台
+展望与积极发展
+最重要的库已经存在,新库不断涌现
+自己的图形引擎
+接口很容易分成单独的模块


-最终安装包较大,因为Dart虚拟机已添加到其中
-使用代码创建接口,这使逻辑和设计之间的界线变得更薄
-比本地开发少的库(和信息)
-不稳定(最近才发布Beta版)


感谢您的关注!

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


All Articles