为安卓开发者介绍的移动开发框架 Flutter

点击上方“程序员大咖”,选择“置顶公众号”

关键时刻,第一时间送达!

640?wxfrom=5&wx_lazy=1

640?wx_fmt=gif&wxfrom=5&wx_lazy=1


我是 TrueFace 的高级软件工程师 Adel Boussaken。今天我要给大家介绍 Flutter 开发。欢迎加入 Dart 阵营。


我将跳过所有有关安装和运行 Flutter 应用程序的部分,直接进行操作,如果您不确定 Flutter 是什么,并且有许多问题,请阅读关于 Flutter 的演进之路。 

Dart


如果你认为 Kotlin 是 Java 的替代,等到你看到 Dart 后,你会发现它是直截了当,没有样板代码的:


  • 默认为public,通过前缀“_” 进行私有化;

  • 集合;点击试用(https://dartpad.dartlang.org/966a339d9bef24d546866f3e437e21e6)

  • 一切都是对象 - 没有“primitives” 

  • 命名参数,可选参数,默认值:点击试用(https://dartpad.dartlang.org/13ab41b2107360973cfc6daee4c0768e)

  • 属性 - 无需在任何地方写“get”方法 

  • 级联 - 一切都是构建者 

  • 使用 strong mode  类型推导 - 只需要在本地写“var” 

  • 命名构造函数,自动分配到字段;点击试用

  • 字符串插值,几种类型的字符串;点击试用

  • dartfmt - 如果不想格式化也可以不用这个工具 


这里有一个 Dart 代码的例子,一个获取 IP 地址的私有方法:

_getIPAddress() async {
   String url = 'https://httpbin.org/ip';       
   var httpClient = createHttpClient();       
   var response = await httpClient.read(url);       
   Map data = JSON.decode(response);       
   String ip = data['origin'];  
}

设计模式


我很确定你已将你的 Android 技巧从无序代码扩展到 MVC、MVP、MVVM,Dagger2、DataBinding 和 Android 架构组件,Flutter 基本上就是一个 V(View),响应式视图,它可以是无状态或有状态的 widget,就连 AppCompatActivity 也是一个 widget。


让我们创建一个非常简单的 Activity,我们需要一个文本视图和一个更新该文本的按钮,下面是 Activity 代码(注意没有 XML):

public class Click extends Activity {   
    int count = 0;       
    protected void onCreate(Bundle bundle) {     
        super.onCreate(bundle);           
        setContentView(R.layout.main);           
        final Button button = (Button) findViewById(R.id.button);           
        final TextView text = (TextView) findViewById(R.id.counter);           
        button.setOnClickListener(new View.OnClickListener() {           
        
            public void onClick(View v) {             
                count++;                   
                text.setText("Button clicked " + counter + " times");             
            }           
       });       
   }  
}

在 Flutter,你不需要操纵视图,就像 setText() 中的代码,你可以使用 state 来替代,并且 Flutter 将会处理好屏幕更新,在 Flutter 中和上述代码等价的是:

import 'package:flutter/material.dart';class Activity extends StatefulWidget {     
 @override     
 ActivityState createState() => new ActivityState();  
}class ActivityState extends State<Activity> {  
  int counter = 0;     
  @override     
  Widget build(BuildContext context) {
      return new Scaffold(
      body: new Center(           
      child: new Column(             
      mainAxisAlignment: MainAxisAlignment.center,             
      children: <Widget>[        
            new Text(                 
            'Button clicked ${counter} times',               
      )
,            new FlatButton(               
        child: new Text('Increment')
,                 
        onPressed: ()
{                   
         setState(() {                     
         counter++;                   
       });                 
      },               
     ),           
    ],           
   ),         
  ),       
 );     
}  
}

你也注意到,我们在 setState 中增加了 counter 的值,一旦 onPressed 被触发(之后是 setState),Flutter 将会使用新的 counter 值来重新渲染整个 widget 和 text 对象。


分离 widget 和 state(对于 Android 开发人员来说)的最大优点是,如果你在 Java 代码中旋转设备或调整窗口大小(支持多窗口),则计数器的值将被重置为零,你必须编写大量重复代码来管理 Activity 的生命周期。而在 Flutter 中,这是不需要的,现成的功能,无需额外的代码。


虽然 Dart 最终编译为 native 代码,但它不会访问硬件设备,例如,如果你要获取用户位置,你必须使用平台通道编写特定平台的代码,与此同时,确实有很多插件来处理这些情况,但你依然希望登录Android Studio(或 Xcode)在代码之间完成该事项。

Async/Await


在 Android 开发中,UI 线程非常重要,完成速度稍慢的事情工作,会显示以下神秘行:

I/Choreographer(1378): Skipped 55 frames!  The application may be doing too much work on its main thread.

这种情况你只能使用线程来处理,如下所示:

public void onClick(View v) {
   new Thread(new Runnable() {       
      public void run() {           
          // a potentially  time consuming task              
          final Bitmap bitmap =                 
                  processBitMap("image.png");               
          mImageView.post(new Runnable() {               
              public void run() {                     
                  mImageView.setImageBitmap(bitmap);                   
              }               
           });         
         }       
       }).start();  
     }

上述代码可以很简单的使用 Dart 完成:

onClick() async {     
 Bitmap bitmap = await processBitMap("image.png");}
// you don't want to update non-existent widget     
if (!mounted) return;}
  setState(() {     
   imageView = bitmap;     
 });  
};

这里发生了什么? async 修饰符标记的方法会安排稍后执行,该方法变成非阻塞的,因此你的应用程序不会丢失任何帧,await 修饰符挂起方法的执行,等待 processBitMap 完成,然后执行将会恢复。

Widget


目前有大量在 Android 之上运行的各种 OEM 软件和皮肤,这都是可以理解的,因为每个制造商都希望为其设备建立自己的附加值,我不能计算我究竟说了多少次“Funck S*ms*ng”,OEM 定制对 Android 开发人员引入了很多难题.。


Flutter,为了实现 120FPS 的目标,已决定开始在一个空的画布上,使用 Skia 2D 图形库绘制每个 widget,而不是重用 OEM widget,并实现大多数 Material Designa 和通用 layout。


这种方法,无法保证你的应用程序在多种设备上看起来是一样的,尽管并不是所有的 Flutter widget 都经过优化,可以在平板电脑上也可以在较小屏幕的设备上正常工作。


无论如何,这种方法也为 UI 设计的创新打开了新的大门,例如,以下的代码可以使得用户改变 Activity 的大小:

class HeroScreen extends StatefulWidget {  
 @override     
 HeroState createState() => new HeroState();  
}class HeroState extends State<HeroScreen> with SingleTickerProviderStateMixin {  
 
 bool _scaling = false;     
 
 Matrix4 _transformScale = new Matrix4.identity();     
 double scalePos = 1.0;  set _scale(double value) =>     
     _transformScale = new Matrix4.identity()..scale(value, value, 1.0);  
     
 @override     
 Widget build(BuildContext context) {    return new Scaffold(    
       body: new GestureDetector(         
           onScaleStart: (details)
{             
            setState(() {             
               _scaling = true;                 
            });               
          },               
          onScaleUpdate: (details) {              var n = details.scale;             
            setState(() {                 
              scalePos = n;                   
              _scale = n;                 
            });               
           },               
           onScaleEnd: (details) {               
            setState(() {                   
            _scaling = false;                   
            _scale = scalePos < 1 ? 1.0 : scalePos;                 
            });               
          },               
          child: new Container(                 
              color: Colors.black26,                   
              child: new Transform(                   
                 transform: _transformScale,                       
                 child: new Center(                       
                    child: new Row(                             
                      mainAxisAlignment: MainAxisAlignment.center,                               
                      children: <Widget>[                          
                      new Text(_scaling ? "Scaling" : "Scale me up"),                           
                    ]))))));     
    }  
 }

这让我们得到一个非常有趣的小部件,这样去写可重复使用的小部件就太容易了, 我可以将以上不太友好的代码转换成可重用且干净的小部件:

class Scale extends StatefulWidget {
 final Widget child;  
 final GestureScaleStartCallback onStart;  
 final GestureScaleEndCallback onEnd;  
 final GestureScaleUpdateCallback onUpdate;  
 
Scale({Key key,
 this.child,
 this.onStart,
 this.onUpdate,
 this.onEnd})     
   : super();     
   
 @override     
 ScaleState createState() => new ScaleState();  
}class ScaleState extends State<Scale> with SingleTickerProviderStateMixin {  
  Matrix4 _transformScale = new Matrix4.identity();     
  double scalePos = 1.0;  set _scale(double value) =>     
      _transformScale = new Matrix4.identity()..scale(value, value, 1.0);  
  
  @override     
  Widget build(BuildContext context) {    return new GestureDetector(     
     onScaleStart: widget.onStart,           
     onScaleUpdate: (details)
{           
       widget.onUpdate(details);          
       var n = details.scale;             
       setState(() {               
       scalePos = n;               
       _scale = n;             
     });           
   },           
   onScaleEnd: (details) {           
     widget.onEnd(details);             
     setState(() {             
       _scale = scalePos < 1 ? 1.0 : scalePos;             
     });           
   },           
   child: new Container(           
      color: Colors.black26,               
      child: new Transform(                 
        transform: _transformScale, child: widget.child)));     
      }  
  }

重构后的代码如下:

class HeroScreen extends StatefulWidget {
    @override     
    HeroState createState() => new HeroState();  
}class HeroState extends State<HeroScreen> with SingleTickerProviderStateMixin {
    bool _scaling = false;     
    
    @override     
    Widget build(BuildContext context) {    return new Scaffold(    
         body: new Scale(       
       onStart: (details)
=> setState(() => _scaling = true),         
    onUpdate: (details) {},         
    onEnd: (details) => setState(() => _scaling = false),         
    child: new Container(       
        color: Colors.black26,             
        child: new Center(             
            child: new Row(                 
                mainAxisAlignment: MainAxisAlignment.center,                     
                children: <Widget>[                new Text(_scaling ? "Scaling" : "Scale me up"),               
            ]))),       
         ));     
        }  
       }

Etc


  • 即时运行称为热重新加载,需要不到一秒钟

  • Flutter 使用 CPU 绘制图形、使用 GPU 渲染, 但是一些关于 3D 的 API 是不可用的

  • 不用 XML 来设计布局

  • 布局只需一次性计算树的大小

  • 即使有语法错误,您的 APP 也不会崩溃

  • Flutter 小部件可以嵌入到 Android Activity 中

  • 您不能在 Flutter 小部件中嵌入 Android 库

  • 至少需要 60 FPS,除非你的机器正在做别的事情

  • 受到 React 的启发设计 了API,但不是像 React 那样运行的

  • 不需要任何 POJO


Flutter 就是这么棒,希望你们能喜欢我写的第一篇博客,谢谢你们阅读到这里。




640.jpeg

  • 来自:开源中国社区

  • 链接:https://www.oschina.net/translate/intro-to-flutter-for-android-developers

  • 原文:https://medium.com/@netdur/intro-to-flutter-for-android-developers-7ff72137cc40

  • 程序员大咖整理发布,转载请联系作者获得授权

640?wx_fmt=gif

640?【点击成为Java大神】

没有更多推荐了,返回首页