Flutter

Flutter 入门使用

2019-10-17  本文已影响0人  GoGoCoding

Flutter

概要

Flutter是一个由谷歌开发的开源移动应用软件开发工具包。它是以dart为基础做出的一套SDK,支持在Android和iOS上构建APP。简单来说它与RN、Weex等众多混合开发平台框架一样,都是一套代码实现多平台发布的跨平台框架。

Flutter与其他跨平台的最大不同之处是它自建了一个2D渲染引擎.

flutter框架
 * Flutter框架可分为Framework层和Engine层;

    - Flutter Framework: 整个框架层都是用Dart语言实现,该层提供一套基础库, 用于处理动画、绘图和手势等。并且基于绘图封装了一套 UI组件库,并且细分为两种风格的组件.
    
        1. Materail : Android风格的Widget;
        2. Cupertino: IOS风格的Widget;

    - Flutter Engine: 这是一个纯 C++实现的框架层,包含了 Skia引擎(高性能渲染引擎)、Dart运行环境、文字排版引擎等。

1. 配置Flutter环境

参考:

flutter 中文网

flutter English

 flutter doctor

1.1 初始化项目

    flutter create myapp 
    cd myapp
    flutter devices
    flutter run -d <deviceID>
   flutter create -t module smarthome_flutter
flutter create -i swift -a kotlin myapp

SmartHome集成flutter 目前采用的是flutter module的模式

1.2 Flutter 版本

#查看当前使用的分支
flutter channel 

#切换分支 stable为flutter的稳定版分支
flutter channel stable
flutter channel master
flutter channel beta
flutter channel dev

1.3 升级 Flutter channel 和 packages

flutter upgrade
#获取pubspec.yaml文件中列出的所有依赖包
flutter packages get
#获取pubspec.yaml文件中列出的所有依赖包的最新版本
flutter packages upgrade

2. 如何集成Flutter

2.1 确保所有的submodule已下载

Native工程下 feature-flutter分支 执行 git submodule update

2.2 生成flutter module的个人本地配置

cd [Your SmartHome Project Path]/smarthome_flutter

flutter run

2.3 cocoapods 集成flutter

回到Native工程的路径下 pod install

2.4 Q&A

*   打开.ios/Runner.xcworkspace -> TARGETS -> Runner      -> Build Phases -> Embed Frameworks  然后删除       flutter.frameworks   

3. 原生和Flutter相互通信 platform channels

通过platform channels 在flutter 和宿主(ios/andriod)之间传递消息,如下图所示:

platform channels

Samples



import 'package:flutter/services.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:smarthome_flutter/common/tools/SHEventBus.dart';
part 'platfrom.g.dart';

var sh_event_bus = EventBus();
class SHPlatform{
  static final MethodChannel channel =  _setupChannel();



  static MethodChannel _setupChannel(){
    var channel = MethodChannel("com.jd.smarthome",StandardMethodCodec());
    
    channel.setMethodCallHandler((MethodCall call) async {
      String method = call.method;
      dynamic arguments = call.arguments;
      switch(method){
        case 'updateInfo':
          sh_event_bus.emit('updateHomePage');
        break;

        default:
          throw new PlatformException(code:"");
      }
    });

    return channel;
  }

  static DeviceInfo _deviceInfo;
  static Future<DeviceInfo> getDeviceInfo() async{
    if(_deviceInfo == null){
      return SHPlatform.channel.invokeMethod("deviceInfo")
      .then((onValue){
        _deviceInfo = DeviceInfo.fromJson(Map<String, dynamic>.from(onValue));
        return _deviceInfo;
      });
    }else{
      return Future.value(_deviceInfo);
    }
  }
}

@JsonSerializable()
class DeviceInfo{
  String platform;
  String hardPlatform;
  String systemVersion;
  String appVersion;
  String channel;
  String deviceId;
  String tgt;
  String pin;
  DeviceInfo();
  factory DeviceInfo.fromJson(Map<String, dynamic> json) => _$DeviceInfoFromJson(json);
}

-(void)showFlutterView{
    FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
    
    __weak __typeof(self) weakSelf = self;
    
    // 要与main.dart中一致
    NSString *channelName = @"com.jd.smarthome";
    
    //FlutterMethodChannel *messageChannel;
    self.messageChannel = [FlutterMethodChannel methodChannelWithName:channelName
                                                                       binaryMessenger:flutterEngine];
    [self.messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        
        if ([call.method isEqualToString:@"toNativeSomething"]) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"flutter回调" message:[NSString stringWithFormat:@"%@",call.arguments] delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
            [alertView show];
            
            // 回调给flutter
            if (result) {
                result(@10);
            }
        } else if ([call.method isEqualToString:@"toNativePush"]) {
            
            //[GLOBAL_Nav pushViewController:vc animated:YES andLastName:getClassName];
            
        } else if ([call.method isEqualToString:@"toNativePop"]) {
            [GLOBAL_Nav popViewControllerAnimated:YES];
        }else if([call.method isEqualToString:@"deviceInfo"]){
            NSDictionary* data = @{
                                   @"platform":kPlatValue,
                                   @"hardPlatform":[CommonMethod getPlatformName],
                                   @"systemVersion":[CommonMethod getSystemVersion],
                                   @"appVersion":[CommonMethod getAppVersion],
                                   @"channel":kChannelValue,
                                   @"deviceId":[OpenUDID value],
                                   @"tgt":[LoginObjectClass sharedLoginObjectClass].A2String,
                                   @"pin":[LoginObjectClass sharedLoginObjectClass].pin,
                                   };
            result(data);
        }else{
            result(FlutterMethodNotImplemented);
        }
    }];
    
    FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
    flutterViewController.navigationItem.title = @"Flutter Demo";
    
    [GLOBAL_Nav pushViewController:flutterViewController animated:YES andLastName:getClassName];
    
    [self updateFlutterHomePage];
}

-(void)updateFlutterHomePage{
    
    [self.messageChannel invokeMethod:@"updateInfo" arguments:@{@"page": @"1"} result:^(id  _Nullable result) {
        
        if([result isKindOfClass:[FlutterError class]]){
    
            FlutterError *error = (FlutterError *)result;
            SHLogError(kLogFlutter, @"updateInfo with error message: %@", error.message);
            
        }else  if (result == FlutterMethodNotImplemented) {
            SHLogError(kLogFlutter, @"updateInfo was unexepectedly not implemented ");
            
        }else{
            SHLogInfo(kLogFlutter, @"updateInfo success");
            
        }
        
    }];
}

4 Flutter 状态管理 ----Provider

4.1 为什么需要状态管理

state1

在 State 属于某一个特定的 Widget,在多个 Widget 之间进行交流的时候,虽然你可以使用 callback 解决,但是当嵌套足够深的话,我们增加非常多可怕的垃圾代码。

随着功能的增加,你的应用程序将会有几十个甚至上百个状态。这个时候应用应该会像下图一样。

这时候,我们便迫切的需要一个架构来帮助我们理清这些关系,状态管理框架应运而生。

state2

4.2 怎么用provider

4.2.1、添加依赖

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  webview_flutter: ^0.3.9+1
  dio: 2.1.10
  json_annotation: ^2.0.0
  provider: ^3.0.0
  intl: ^0.15.7
  crypto: ^2.0.6
  logging:
  cached_network_image: ^1.1.1
  flutter_easyrefresh: ^1.2.7

4.2.2、创建数据Model

//这里使用了 mixin 混入了 ChangeNotifier,这个类能够帮助我们自动管理所有listeners。当调用 notifyListeners() 时,它会通知所有听众进行刷新。

class CounterModel with ChangeNotifier {
  int _count = 0;
  int get value => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

4.2.3、创建顶层共享数据

class GlobalConfig{
  static get version => '0.0.3';
  static get counter => CounterModel();

  static ThemeData get themeData => ThemeData(
    primaryColor: Color(0xff181B34),
    textTheme: TextTheme(
      title: TextStyle(
        fontSize: 14,
        color: Colors.white
      )
    )
  );

  static List<SingleChildCloneableWidget> get providers => [
    Provider<String>.value(value:GlobalConfig.version),
    ChangeNotifierProvider<CounterModel>.value(
      value: GlobalConfig.counter,
    )
  ];
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: GlobalConfig.providers,
      child: MaterialApp(
        title: 'SmartHome demo',
        theme: GlobalConfig.themeData,
        home: MyHomePage(
          title: "APP",
        ),
        debugShowCheckedModeBanner: false,
      ),
    );
  }
}

4.2.4、在子页面中获取状态

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final _counter = Provider.of<CounterModel>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('FirstPage'),
      ),
      body: Center(
        child: Text(
          'Value: ${_counter.value}',
          style: TextStyle(fontSize: 11),
        ),
      ),    
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  
  
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Page'),
      ),
      body: Consumer2<CounterModel,string>(
        builder: (context, CounterModel counter, string version, _) => Center(
              child: Text(
                'Value: ${counter.value}' + version,
                style: TextStyle(fontSize: 11,
               ),
            ),
         ),
      ),
    );
  }
}

5 Map -> model

介绍一下官方推荐的json_serializable package包。 它是一个自动化的源代码生成器,可以在开发阶段为我们生成JSON序列化模板.

5.1、在项目中设置json_serializable

dependencies:
  # Your other regular dependencies here
  json_annotation: ^2.0.0

dev_dependencies:
  # Your other dev_dependencies here
  build_runner: ^1.0.0
  json_serializable: ^3.0.0

5.2 以json_serializable的方式创建model类

import 'package:json_annotation/json_annotation.dart';
part 'skill_model.g.dart';

@JsonSerializable()
class SkillList {
  SkillList();
  int page_size;
  int sum_size;
  int current_page;

  List<SkillListItem> skills;
  factory SkillList.fromJson(Map<String, dynamic> json) => _$SkillListFromJson(json);
  Map<String, dynamic> toJson() => _$SkillListToJson(this);  
}
//一个SkillList.fromJson 构造函数, 用于从一个map构造出一个 User实例 map structure
//一个toJson 方法, 将 User 实例转化为一个map.

jsonError
#一次性生成
flutter packages pub run build_runner build

#持续生成
flutter packages pub run build_runner watch

request.parseFunction = (json){
      SkillList tmp = SkillList.fromJson(json);
      Map<String, dynamic> newMap = tmp.toJson();
      return SkillList.fromJson(json);
    };

6 异步async、await和Future

Flutter中,虽然Dart是基于单线程模型的,但是这并不意味着我们没法完成异步操作。在Dart中我们可以通过async关键字来声明一个异步方法,在异步方法中可以使用await表达式挂起该异步方法中的某些步骤,从而实现等待某步骤完成的目的; Future是Dart中提供的一个类,它用于封装一段在将来会被执行的代码逻辑。

Note

end

推荐

flutter 中文

flutter English

flutter github

Dart Packages

An open list of apps built with Flutter

完整项目 Flutter / RN / Kotlin / Weex

上一篇 下一篇

猜你喜欢

热点阅读