Flutter动态创建UI实现方案
flutter_dynamic
flutter_dynamic 是一个能动态创建Flutter应用的引擎。flutter_dynamic不但支持写UI,还支持写代码逻辑。
EN: The flutter_dynamic is an engine that create flutter application dynamically. flutter_dynamic not only supports writing UI, but also writing code logic.
-
153768151@qq.com - feel free to contact me
Best practice
动态创建类似有UI和交互的页面。如果你已经阅读了该文档,可以跟着我们一步步创建一个有趣的动态页面。Best practice
<img src="https://img.haomeiwen.com/i3868052/24e08253efeff413.gif?imageMogr2/auto-orient/strip" width="320px"/>
目录 Table of contents
- General-info概况
- Install 安装
- Get-started 使用
- Grammar 语法
- Widgets 组件
- Customize-Widget 自定义组件
- Customize-Grammar 自定义语法
- Contact 联系
General-info
有时候在产品开发的过程中需要远程动态地创建表单或更新某个表单应用,但Flutter框架不像javascript动态语言可以远程下发并动态执行脚本,也不像Android或iOS平台的java或objective c语言支持动态热更新或动态语言特性等,甚至在Flutter中限制了Dart语言的反射特性,所以基于Flutter来做动态页面就显得很有局限性。开发flutter_dynamic引擎一是为了满足产品需要远程动态创建表单应用的需求,二是借此机会探索Flutter更广泛的动态特性实现方案;目前这个引擎还处在不断完善阶段,难免会有缺陷。
本引擎主要包括以下几个方面:
--提供高度类似Flutter系统的Widget类型,以满足构造UI所需的Widget库;并提供自定义Widget扩展引擎,满足开发者自定义基于本引擎解释的Widget类型;
--提供variable变量解释引擎,提供类似Dart基本类型语法,满足类似变量定义、类属性定义等;
--提供action方法解释引擎,提供类似Dart方法语法,满足类似方法调用;
--提供code代码解释引擎,提供类似Dart关系运算、逻辑运算、控制流程等语法,满足写基本的代码逻辑;
--提供event事件解释引擎,提供类似Flutter的事件处理;
Install
step 1. Depend on it
dependencies:
yz_flutter_dynamic:
git:
url: https://github.com/fisherjoe/flutter_dynamic.git
ref: master
Or
Download or clone the code to the local:
dependencies:
yz_flutter_dynamic:
path: your/code/path
Or
Do as example of code.
step 2. Use it
Use as common flutter plugin in flutter project.
step 3. Import it
Now in your Flutter code, you can use:
import 'package:yz_flutter_dynamic/main.dart';
Get-started
flutter_dynamic是完全开源的,在代码中的example里也已经具有完整的示例,以下针对核心的流程说明:
Hello world
import 'package:flutter/material.dart';
import 'package:yz_flutter_dynamic/main.dart';
class Helloworld extends StatefulWidget {
@override
_HelloworldState createState() => _HelloworldState();
}
class _HelloworldState extends State<Helloworld> {
@override
Widget build(BuildContext context) {
return YZDynamic.buildWidget(
context,
hwDsl,
);
}
}
Map hwDsl =
{
"xKey": "",
"widgetName": "Scaffold",
"props": {
"appBar": {
"xKey": "",
"widgetName": "AppBar",
"props": {
"title": {
"widgetName": "Text",
"props": {"data": "Navigator"}
}
}
},
"body": {
"xKey": "",
"widgetName": "SafeArea",
"props": {
"child": {
"xKey": "_Text",
"widgetName": "Text",
"props": {
"data": "Hello world"
}
}
}
}
}
};
How to create your dynamic widget
我们认为Flutter的任何页面或组件都是StatefulWidget,所以当我们无论创建一个新页面或组件时我们都把它当作一个StatefulWidget来看待。页面和StatefulWidget是一样。
step1: How to create widget UI
invoke
YZDynamic.buildPage(context, json, preConfig: preConfig);
json
{
"xKey": "globalKeyOfWidget",
"widgetName": "Container",
"props": {
"padding": "[10, 10, 0, 0]",
"height": "44",
"child": {
"widgetName": "Text",
"props": {
"data": "Data of Text widget",
"color": "0xfff1f1f1"
}
}
},
"xEvents": [
{
"eventType": "onClick",
"code": '''
[code...]
'''
},
"xVar": {
}
]
}
上面的代码较完整地呈现了一个构造一个widget的json数据。其中rootWidget的值描述了该widget的根结点Container,返回StatefulWidget包裹的Container。下面将主要对各属性一一说明:
-
xKey
:The globalkey of widget to find the widget. -
widgetName
: The type of widget. -
xEvents
: The event of the widget support "onClick" type now. The value support grammar as Grammar 语法 -
xVar
: Define the variable of widget. -
props
: The properties of the widget which property is the same as system of widget's property. There are two types of props value such as :- string: The type of property value like "color" "width" should use string type {"widget":"100", "color": "#ff000000"}. Specially the type of EdgeInsets will be "[10, 20, 30, 40]" for "[left, top, right, bottom]". The enum of type will be like "multiline" for TextInputType.multiline
- map: The type of property value like "Size" will be {"color":"", "width":""}.
step2: How to create page
创建page UI和创建widget UI是一样的,同时它们的json数据也是通用的,widget是page的简单化版。
invoke build page
YZDynamic.buildPage(context, json, preConfig: preConfig);
invoke present page
YZDynamic.handle(context, json, preConfig: preConfig)
json
{
"page": {
"key": "",
"presentMode": "",
"state": {
"initState": [
],
"build": [
],
"dispose": [
]
},
"xVar":{},
"rootWidget": {}
}
}
上面的代码较完整地呈现了一个构造一个page的json数据。 page属性里有些和widget不致,下面一一说明:
-
key
:The globalkey of page. -
presentMode
: The present mode of page. The value is "dialog / navpage".Use for YZDynamic.handle invocation only. -
state
: The lifecycle of page which is as good as the system StatefulWidget. -
xVar
: Define the variable of widget. -
rootWidget
: The root widget of page. It is similay to the return widget of StatefulWidget's build method.
step3: How to write code
flutter_dynamic提供了一套仿Dart语言的伪代码code, 通过伪代码code可以实现页面更加复杂的逻辑。code可以定义变量、控制流程、获取不同组件之间数据、调用Flutter system api等等能力。json数据的以下地方可以使用code:Grammar 语法
- code of property: such as event config {"eventType": "onClick", "code": ``` [code...] ```}
- other value: start with "code:", such as {"color": ``` code: [code...] ```}
step4: How to use variables
伪代码code变量有四种作用域:分别是页面作用域、组件作用域、code块作用域;使用变量的方式分别是:<p:variableName>、<w:variableName>、<c:variableName>。有如下方式可以初始化变量:
- 在page/widget对象的xVar属性中定义,如:"xVar": {"variableName": "variableValue"};
- 在code中定义,如:```<c:variableName>=Int(10); <w:variableName2>=String(Hellow world)```;
注意:伪代码code变量没有变量类型。如果赋值时没有强制说明类型则自动默认为字符串,使用的时候会根据特定需要自动转换。
step5: How to implement event
引擎提供了一套简单的事件处理机制xEvent。通过事件处理机制可以定义事件及实现事件的逻辑。
事件的逻辑是通过伪代码code来实现的。
Grammar
在源代码的example里有写动态页面的伪代码语法,如图所示:
Simulator Screen Shot - iPhone 12 - 2020-12-17 at 09.22.41.pngWidgets
目前我们支持如下Widget。
Container
Text
TextField
Column
Expanded
Image
Padding
Row
SafeArea
SingleChildScrollView
Scaffold
AppBar
RawMaterialButton
SizedBox
Dialog
Customize-Widget
当引擎提供的组件不满足使用需求时,可以通过扩展组件的方式添加自定义的组件。自定义组件的方式如下,或者参考 Container:
Define CustomerWidget
/// CustomerWidget handler
class YZCustomerWidgetHandler extends YZDynamicBasicWidgetHandler {
@override
String get widgetName => 'CustomerWidget';
@override
Widget build(Map<String, dynamic> json, {Key key, BuildContext buildContext}) {
return _Builder(json, key:key);
}
}
class _Builder extends YZDynamicBaseWidget {
final Map<String, dynamic> json;
_Builder(this.json, {Key key}): super(json, key: key);
@override
_BuilderState createState() => _BuilderState();
}
class _BuilderState extends YZDynamicWidgetBasicState<_Builder> {
//Deal with props / 处理控件属性
Map props;
Alignment _alignment;
@override
void initState() {
super.initState();
//Deal with props / 处理控件属性
props = super.config.props;
_alignment = YZDinamicWidgetUtils.alignmentAdapter(props["alignment"]);
}
@override
Widget build(BuildContext context) {
Widget _widget;
CustomerWidget _subwidget = CustomerWidget(
alignment: _alignment,
);
//Deal with events / 处理事件
_widget = super.buildWithEvents(_subwidget, super.config.xEvents);
return _widget;
}
@override
void registerActions() {
//Deal with action / 处理事件实现
actionFunctions['setState'] = stateSetter;
}
void stateSetter({
Map params,
YZDynamicRequest request,
List<YZDynamicActionRule> rules,
Map localVariables,
State state,
}) {
print('Execute xAction: ${this.runtimeType} setState');
if (mounted) {
setState(() {});
}
}
}
Register CustomerWidget
YZDynamicCommon.reginsterWidgetHandler(YZCustomerWidgetHandler)
User CustomerWidget
{
"xKey": "globalKeyOfWidget",
"widgetName": "CustomerWidget",
"props": {
"alignment": "[10, 10, 0, 0]"
},
"xEvents": [
{
"eventType": "onClick",
"code": '''
[code...]
'''
},
"xVar": {
}
]
}
Customize-Grammar
当引擎提供的伪代码语法不满足使用需求时,可以通过扩展action的方式添加自定义的语法。
Define action
/*
* Custum action
* 自定义 action
*/
class YZToastHandler extends YZDynamicPublicActionHandler{
@override
void action(BuildContext context, {
Map params,
YZDynamicRequest request,
List<YZDynamicActionRule> rules,
Map localVariables,
State state,
}) {
String tip = params['tip'];
Fluttertoast.showToast(
msg: tip,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
}
@override
String get actionName => 'yzToast';
}
Register action
YZDynamicCommon.reginsterPublicActionHandler(YZToastHandler());
User action
yzToast(tip:content);
Contact
Created by 153768151@qq.com - feel free to contact me