需求说明:根据JSON数据来配置页面中的组件

    数据部分:

    1. [
    2. {
    3. "formId":"titleInput",
    4. "genre":"schemeName",
    5. "value":"高蛋白减重饮食方案",
    6. "label":"名称",
    7. "placeholder":"请输入方案名称",
    8. "max":"",
    9. "min":""
    10. },
    11. {
    12. "formId":"assignTime",
    13. "genre":"executionTime",
    14. "type":"2",
    15. "value":"0"
    16. },
    17. {
    18. "formId":"checkBox",
    19. "genre":"feedbackMode",
    20. "label":"反馈方式",
    21. "value":[
    22. ],
    23. "options":[
    24. {
    25. "title":"打卡反馈",
    26. "value":"clockInFeedback",
    27. "enable":1,
    28. "checked":1
    29. },
    30. {
    31. "title":"图片反馈",
    32. "value":"pictureFeedback",
    33. "enable":1,
    34. "checked":1
    35. },
    36. {
    37. "title":"文字反馈",
    38. "value":"textFeedback",
    39. "enable":1,
    40. "checked":1
    41. },
    42. {
    43. "title":"视频反馈",
    44. "value":"videoFeedback",
    45. "enable":1,
    46. "checked":1
    47. },
    48. {
    49. "title":"记录饮食",
    50. "value":"dietFeedback",
    51. "enable":1,
    52. "checked":1
    53. },
    54. {
    55. "title":"记录运动",
    56. "value":"exerciseFeedback",
    57. "enable":0,
    58. "checked":0
    59. },
    60. {
    61. "title":"记录血糖",
    62. "value":"sugarFeedback",
    63. "enable":0,
    64. "checked":0
    65. },
    66. {
    67. "title":"记录血压/心率",
    68. "value":"pressureFeedback",
    69. "enable":0,
    70. "checked":0
    71. },
    72. {
    73. "title":"记录体重",
    74. "value":"weightFeedback",
    75. "enable":0,
    76. "checked":0
    77. },
    78. {
    79. "title":"记录睡眠",
    80. "value":"sleepFeedback",
    81. "enable":0,
    82. "checked":0
    83. },
    84. {
    85. "title":"记录用药",
    86. "value":"drugFeedback",
    87. "enable":0,
    88. "checked":0
    89. },
    90. {
    91. "title":"记录体温",
    92. "value":"animalHeatFeedback",
    93. "enable":0,
    94. "checked":0
    95. },
    96. {
    97. "title":"提交问卷",
    98. "value":"questionnaireFeedback",
    99. "enable":0,
    100. "checked":0
    101. }
    102. ]
    103. },
    104. {
    105. "formId":"dietAdvice",
    106. "genre":"dietAdvice",
    107. "options":[
    108. {
    109. "itemName":"早餐",
    110. "advice":"饮食要求:xxx",
    111. "enable":1,
    112. "checked":1,
    113. "foodList":[
    114. {
    115. "foodName":"乳清蛋白粉",
    116. "foodCount":"10",
    117. "foodUnit":"克"
    118. },
    119. {
    120. "foodName":"可溶性膳食纤维",
    121. "foodCount":"10",
    122. "foodUnit":"克"
    123. },
    124. {
    125. "foodName":"温水",
    126. "foodCount":"200",
    127. "foodUnit":"毫升"
    128. },
    129. {
    130. "foodName":"多种营养素制剂",
    131. "foodCount":"1",
    132. "foodUnit":"粒"
    133. },
    134. {
    135. "foodName":"鱼油胶囊(1克)",
    136. "foodCount":"1",
    137. "foodUnit":"粒"
    138. }
    139. ]
    140. },
    141. {
    142. "itemName":"午餐",
    143. "advice":"饮食要求:xxx ",
    144. "enable":1,
    145. "checked":1,
    146. "foodList":[
    147. {
    148. "foodName":"主食(粗细搭配)",
    149. "foodCount":"50",
    150. "foodUnit":"克"
    151. },
    152. {
    153. "foodName":"蛋白质类食物(瘦肉50克=1个鸡蛋或豆腐100克或豆干50克)",
    154. "foodCount":"50",
    155. "foodUnit":"克"
    156. },
    157. {
    158. "foodName":"蔬菜(合计)",
    159. "foodCount":"100",
    160. "foodUnit":"克"
    161. },
    162. {
    163. "foodName":"新鲜叶类蔬菜",
    164. "foodCount":"50",
    165. "foodUnit":"克"
    166. },
    167. {
    168. "foodName":"新鲜菇类",
    169. "foodCount":"50",
    170. "foodUnit":"克"
    171. }
    172. ]
    173. },
    174. {
    175. "itemName":"晚餐",
    176. "advice":"饮食要求:xxx ",
    177. "enable":1,
    178. "checked":1,
    179. "foodList":[
    180. {
    181. "foodName":"主食(粗细搭配)",
    182. "foodCount":"50",
    183. "foodUnit":"克"
    184. },
    185. {
    186. "foodName":"蛋白质类食物(瘦肉50克=1个鸡蛋或豆腐100克或豆干50克)",
    187. "foodCount":"50",
    188. "foodUnit":"克"
    189. },
    190. {
    191. "foodName":"蔬菜(合计)",
    192. "foodCount":"100",
    193. "foodUnit":"克"
    194. },
    195. {
    196. "foodName":"新鲜叶类蔬菜",
    197. "foodCount":"50",
    198. "foodUnit":"克"
    199. },
    200. {
    201. "foodName":"新鲜菇类",
    202. "foodCount":"50",
    203. "foodUnit":"克"
    204. },
    205. {
    206. "foodName":"多种营养素制剂",
    207. "foodCount":"1",
    208. "foodUnit":"粒"
    209. },
    210. {
    211. "foodName":"鱼油胶囊(1克)",
    212. "foodCount":"1",
    213. "foodUnit":"粒"
    214. }
    215. ]
    216. }
    217. ]
    218. },
    219. {
    220. "formId":"upload",
    221. "genre":"imageTask",
    222. "label":"图片任务",
    223. "value":[
    224. "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fs1.sinaimg.cn%2Forignal%2F48ae37a6285f3d6e96840&refer=http%3A%2F%2Fs1.sinaimg.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621837959&t=0bb081b98fe8ecaa89f3037dfcdbfa9b",
    225. "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_bt%2F0%2F13446502320%2F1000&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621838030&t=5e89a1bbbb62e2a491a64c9a0df4dcf9"
    226. ],
    227. "uploadType":"image",
    228. "minCount":"",
    229. "maxCount":"",
    230. "minSize":"",
    231. "maxSize":""
    232. },
    233. {
    234. "formId":"upload",
    235. "genre":"videoTask",
    236. "label":"视频任务",
    237. "value":[
    238. "https://vod.pule.com/6c992c3bvodcq1500003583/4b4fd9cc5285890813365762540/f0.mp4"
    239. ],
    240. "uploadType":"video",
    241. "minCount":"",
    242. "maxCount":"",
    243. "minSize":"",
    244. "maxSize":""
    245. },
    246. {
    247. "formId":"messageNotice",
    248. "genre":"messageNotice",
    249. "label":"消息通知",
    250. "timeLabel":"发送时间",
    251. "messageValue":"",
    252. "timeValue":"",
    253. "placeholder":"请输入通知内容",
    254. "maxLine":"",
    255. "maxCount":""
    256. }
    257. ]

    组件工厂:

    1. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/assignTime.dart';
    2. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/checkBox.dart';
    3. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/checkBoxWrap.dart';
    4. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/dietAdvice.dart';
    5. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/drugArray.dart';
    6. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/itemArray.dart';
    7. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/messageNotice.dart';
    8. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/radioBox.dart';
    9. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/textarea.dart';
    10. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/timePeriod.dart';
    11. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/titleInput.dart';
    12. import 'package:doctor_admin/pages/group/carePlan/schemeComponents/upload.dart';
    13. import 'package:flutter/material.dart';
    14. componentFactor (config) {
    15. if(config['formId'] == 'titleInput') {
    16. var _key = GlobalKey<TitleInputState>();
    17. return {
    18. 'widget': TitleInput(key: _key, config: config,),
    19. 'widgetName': 'titleInput',
    20. 'getResult': () => _key.currentState.result,
    21. };
    22. } else if (config['formId'] == 'assignTime') {
    23. var _key = GlobalKey<AssignTimeState>();
    24. return {
    25. 'widget': AssignTime(key: _key, config: config,),
    26. 'widgetName': 'assignTime',
    27. 'getResult': () => _key.currentState.result,
    28. };
    29. } else if (config['formId'] == 'checkBox') {
    30. var _key = GlobalKey<CheckBoxState>();
    31. return {
    32. 'widget': CheckBox(key: _key, config: config,),
    33. 'widgetName': 'checkBox',
    34. 'getResult': () => _key.currentState.result,
    35. };
    36. } else if (config['formId'] == 'checkBoxWrap') {
    37. var _key = GlobalKey<CheckBoxWrapState>();
    38. return {
    39. 'widget': CheckBoxWrap(key: _key, config: config,),
    40. 'widgetName': 'checkBoxWrap',
    41. 'getResult': () => _key.currentState.result,
    42. };
    43. } else if (config['formId'] == 'radio') {
    44. var _key = GlobalKey<RadioBoxState>();
    45. return {
    46. 'widget': RadioBox(key: _key, config: config,),
    47. 'widgetName': 'radio',
    48. 'getResult': () => _key.currentState.result,
    49. };
    50. } else if (config['formId'] == 'dietAdvice') {
    51. var _key = GlobalKey<DietAdviceState>();
    52. return {
    53. 'widget': DietAdvice(key: _key, config: config,),
    54. 'widgetName': 'checkBox',
    55. 'getResult': () => _key.currentState.result,
    56. };
    57. } else if (config['formId'] == 'textarea') {
    58. var _key = GlobalKey<TextareaState>();
    59. return {
    60. 'widget': Textarea(key: _key, config: config,),
    61. 'widgetName': 'textarea',
    62. 'getResult': () => _key.currentState.result,
    63. };
    64. } else if (config['formId'] == 'messageNotice') {
    65. var _key = GlobalKey<MessageNoticeState>();
    66. return {
    67. 'widget': MessageNotice(key: _key, config: config,),
    68. 'widgetName': 'messageNotice',
    69. 'getResult': () => _key.currentState.result,
    70. };
    71. } else if (config['formId'] == 'upload') {
    72. var _key = GlobalKey<UploadState>();
    73. return {
    74. 'widget': Upload(key: _key, config: config,),
    75. 'widgetName': 'upload',
    76. 'getResult': () => _key.currentState.result,
    77. };
    78. } else if (config['formId'] == 'itemArray') {
    79. var _key = GlobalKey<ItemArrayState>();
    80. return {
    81. 'widget': ItemArray(key: _key, config: config,),
    82. 'widgetName': 'itemArray',
    83. 'getResult': () => _key.currentState.result,
    84. };
    85. } else if (config['formId'] == 'drugArray') {
    86. var _key = GlobalKey<DrugArrayState>();
    87. return {
    88. 'widget': DrugArray(key: _key, config: config,),
    89. 'widgetName': 'drugArray',
    90. 'getResult': () => _key.currentState.result,
    91. };
    92. } else if (config['formId'] == 'timePeriod') {
    93. var _key = GlobalKey<TimePeriodState>();
    94. return {
    95. 'widget': TimePeriod(key: _key, config: config,),
    96. 'widgetName': 'timePeriod',
    97. 'getResult': () => _key.currentState.result,
    98. };
    99. }else {
    100. return {
    101. 'widget': Center(child: Text('暂无对应组件', style: TextStyle(fontSize: 18, color: Color(0xff666666)),),),
    102. };
    103. }
    104. }

    根据数据生成组件:

    1. workComponents(configJson) {
    2. components = [];
    3. configJson.forEach((value) {
    4. components.add(componentFactor(value));
    5. });
    6. setState(() {});
    7. }

    渲染组件:

    1. Column(
    2. children: <Widget>[
    3. for(int i = 0; i < components.length; i++) components[i]['widget'],
    4. ]
    5. )

    获取组件中的表单数据:

    1. var result = [];
    2. for(int i = 0; i < components.length; i++) {
    3. if(components[i]['getResult'] != null) {
    4. var value = components[i]['getResult']();
    5. var tip = checkParams(value); // 检查数据是否完整
    6. if(tip != 'success') {
    7. showMsg(context, tip);
    8. return;
    9. }
    10. result.add(value);
    11. }
    12. }

    组件内容,这里例举其中1个:

    1. import 'dart:convert';
    2. import 'package:doctor_admin/config/common.dart';
    3. import 'package:flutter/material.dart';
    4. class TitleInput extends StatefulWidget {
    5. final config;
    6. TitleInput({Key key, @required this.config}):super(key: key);
    7. TitleInputState createState() => TitleInputState();
    8. }
    9. class TitleInputState extends State<TitleInput> {
    10. TextEditingController _ctrl =TextEditingController(text: '');
    11. FocusNode _node = FocusNode();
    12. var result;
    13. @override
    14. void initState() {
    15. super.initState();
    16. _ctrl = TextEditingController(text: widget.config['value']??'');
    17. setState(() {
    18. result = jsonDecode(jsonEncode(widget.config));
    19. });
    20. }
    21. @override
    22. Widget build(BuildContext context) {
    23. return result == null ? SizedBox() : Container(
    24. color: Color(0xffffffff),
    25. margin: EdgeInsets.only(bottom: setSize(20)),
    26. padding: EdgeInsets.fromLTRB(setSize(36), setSize(40), setSize(36), setSize(40)),
    27. child: Row(
    28. crossAxisAlignment: CrossAxisAlignment.start,
    29. children: <Widget>[
    30. result['label'].isEmpty ? SizedBox() : Container(
    31. padding: EdgeInsets.symmetric(vertical: setSize(20)),
    32. child: Text.rich(
    33. TextSpan(
    34. children: [
    35. // TextSpan(text: '* ', style: TextStyle(color: Color(0xffF76565))),
    36. TextSpan(text: result['label'], style: TextStyle(color: Color(0xff4c4c4c))),
    37. ]
    38. ),
    39. style: TextStyle(fontSize: setFont(28)),
    40. ),
    41. ),
    42. SizedBox(width: setSize(40)),
    43. Expanded(
    44. child: Container(
    45. height: setSize(80),
    46. decoration: BoxDecoration(
    47. border: Border(bottom: BorderSide(width: setSize(1), color: Color(0xffe2e2e2))),
    48. ),
    49. alignment: Alignment.centerLeft,
    50. child: TextField(
    51. style: TextStyle(fontSize: setFont(28), color: Color(0xff222222), fontWeight: FontWeight.w400, height: 1, textBaseline: TextBaseline.alphabetic),
    52. scrollPadding: EdgeInsets.zero,
    53. decoration: InputDecoration(
    54. contentPadding: EdgeInsets.zero,
    55. isDense: true,
    56. border: InputBorder.none,
    57. hintText: result['placeholder']??'',
    58. hintStyle: TextStyle(fontSize: setFont(28), color: Color(0xffCCCCCC), height: 1, textBaseline: TextBaseline.alphabetic),
    59. counterText: '',
    60. ),
    61. controller: _ctrl,
    62. focusNode: _node,
    63. keyboardType: TextInputType.text,
    64. autocorrect: false,
    65. maxLength: ('${result['max']??''}').isEmpty ? 10000 : result['max'],
    66. maxLines: 1,
    67. textInputAction: TextInputAction.done,
    68. onChanged: (value) => {
    69. result['value'] = value
    70. },
    71. ),
    72. ),
    73. )
    74. ],
    75. ),
    76. );
    77. }
    78. }