跨平台方案 |Flutter |Flutter与RN简单对比
大佬文档
源码对应的git地址 platform_rn_flutter
跨平台方案
对比项 | RN | flutter |
---|
当前版本 | v0.63.3 | 1.23.0-19.0.pre.20 |
start | 90.8K | 105K |
issues | 810 | 5K+(0.29天) |
环境配置 | npm、node、react-native-cli | flutter_sdk |
运行环境 | JSCore | Flutter Engine |
语言 | JavaScript | dart |
编译器 | WebStorm/VSCode、AndroidStudio、XCode | AndroidStudio(VSCode)、XCode |
android-gradle | 6.2 | 5.6.2 |
新建用时 | 02:34.17 | 00:21.22 |
启动用时(包拉取完成) | 30S | 14S |
混合开发 | JavaModule | MethodChannel |
环境搭建
共同需要
Android和ios开发环境
React Native
npm、node、react-native-cli等
Flutter
flutter-sdk、编译器插件
对比发现,flutter环境搭建较为简单,失败率低于RN
实现原理
- RN 通过JS配置页面布局,最终解析渲染为原生控件
- flutter 使用原生的画布,所有的控件都是自己绘制
开发
语言对比
1 2 3 4 5 6 7 8 9 10 11 12
| var a = 1
async function doSomeThing() { var result = await xxxx() doAsync().then((res) => { console.log("ffff") }) } function* _loadUserInfo () { console.log("**********************"); yield put(UpdateUserAction(res.data)); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| var a = 1;
void doSomeThing() async { var result = await xxxx(); doAsync().then((res) { print('ffff'); }); } _loadUserInfo() async* { print("**********************"); yield UpdateUserAction(res.data); }
|
动态语言和非动态语言都有各种的优缺点,比如 JS 开发便捷度明显会高于 Dart ,而 Dart 在类型安全和重构代码等方面又会比 JS 更稳健。
界面开发
最大的区别在于flutter的平台无关性,因为RN最终渲染的是原生UI,所有在不同的端上表现的是不同的效果;flutter渲染的结果是所有的端上,都是相同的,如果需要不同的效果,需要做单独处理。
状态管理
这一点两个平台是很接近的,甚至可以说是跟着RN走的
1 2 3 4 5 6 7 8 9 10 11 12 13
| this.state = { name: "" };
···
this.setState({ name: "loading" });
···
<Text>this.state.name</Text>
|
1 2 3 4 5 6 7 8 9
| var name = "";
setState(() { name = "loading"; });
···
Text(name)
|
性能比较
60fps
混合开发
flutter
1
| var platform = const MethodChannel('samples.flutter.io/battery');
|
调用原生方法
1 2 3
| void _incrementCounter() async { await platform.invokeMethod('getBatteryLevel'); }
|
原生写法,对应act里面添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private val CHANNEL = "samples.flutter.io/battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getBatteryLevel") { startActivity(Intent(this, HomeActivity::class.java)) result.success("") } else { result.notImplemented() } }
}
|
原生方法调用
1 2
| new MethodChannel(Objects.requireNonNull(getFlutterEngine()).getDartExecutor(), CHANNEL).invokeMethod("aaa", "c");
|
flutter触发调用
1 2 3 4 5 6 7 8
| platform.setMethodCallHandler((handler) { switch (handler.method) { case "aaa": print(handler); break; } return null; });
|
React Native
调用原生方法----官方示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class ToastModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext context; private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) { super(reactContext); context = reactContext; }
@NonNull @Override public String getName() { return "ToastExample"; }
@Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG); return constants; }
@ReactMethod public void show(String message, int duration, Callback callback) { Toast.makeText(getReactApplicationContext(), message, duration).show(); callback.invoke("success"); } }
|
新建CustomToastPackage—这个应该是可以复用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class CustomToastPackage implements ReactPackage {
@Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); }
@Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));
return modules; }
}
|
1 2 3 4 5 6 7 8 9
| @Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new CustomToastPackage()); return packages; }
|
为了方便或者说规范,新建ToastExample.js
1 2 3 4
| import { NativeModules } from 'react-native';
export default NativeModules.ToastExample;
|
1 2 3 4 5 6
| import ToastExample from './ToastExample';
ToastExample.show('Awesome', ToastExample.SHORT, (msg)=>{ console.log(msg); });
|
界面搭建与简单封装
flutter—源码上传git
flutter 可以通过扩展函数做一些简单的封装,简化开发量,简单封装后的部分写法
权限请求
1 2 3 4 5
| runWithPermission( [Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE], () { });
|
网络请求
1 2 3 4 5 6 7 8
| HttpManager.getInstance().post( "user/login", {"username": "13812345678", "password": "654321"}, (data) { }, (error) { print(error); });
|
全屏/展示状态栏
1 2 3 4
| import 'package:base_flutter/utils/libs_utils_common.dart'; CommonHelper.fullscreen(); CommonHelper.normal();
|
UI布局的问题:
布局现有写法,嵌套层次太多,比较麻烦,可以使用扩展函数封装,实现链式调用。下面的例子是网络示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Test extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Demo'),), body: buildItem("amy") .addNeighbor(buildItem("billy"),) .intoListView() .intoOffstage(offstage: false) .intoContainer() ); }
Container buildItem(String name) { return Icon(Icons.phone) .addNeighbor(Text(name)) .intoRow(crossAxisAlignment: CrossAxisAlignment.center,) .intoContainer(color: Colors.white, padding: EdgeInsets.all(20),); } }
|