Flutter动态JSON解析生成UI插件json_dynamic_widget的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter动态JSON解析生成UI插件json_dynamic_widget的使用

json_dynamic_widget 是一个强大的Flutter库,它允许你通过JSON或YAML结构动态地创建和更新Flutter UI。这使得在运行时根据不同的配置文件构建界面成为可能,并且可以极大地提高开发效率和灵活性。

目录

Live Example

你可以在线查看Web示例

First Party Plugins

以下是此库提供的官方插件列表:

IDE Extensions

Visual Studio Code

Migration to 7.X.X version

从6.x版本迁移到7.x版本时,需要注意一些破坏性变更。这些变更可以通过Migration CLI自动完成。

Code Generation

7.0.0 版本开始,引入了代码生成器来简化动态小部件的创建。下面展示了 >= 7.0.0< 7.0.0 的差异。

>= 7.0.0

import 'package:json_dynamic_widget/json_dynamic_widget.dart';

part 'json_column_builder.g.dart';

@jsonWidget
abstract class _JsonColumnBuilder extends JsonWidgetBuilder {
  const _JsonColumnBuilder({
    required super.args,
  });

  @override
  Column buildCustom({
    ChildWidgetBuilder? childBuilder,
    required BuildContext context,
    required JsonWidgetData data,
    Key? key,
  });
}

< 7.0.0

import 'package:child_builder/child_builder.dart';
import 'package:flutter/material.dart';
import 'package:json_dynamic_widget/json_dynamic_widget.dart';
import 'package:json_theme/json_theme.dart';
import 'package:json_theme/json_theme_schemas.dart';

class JsonColumnBuilder extends JsonWidgetBuilder {
  const JsonColumnBuilder({
    required this.crossAxisAlignment,
    required this.mainAxisAlignment,
    required this.mainAxisSize,
    this.textBaseline,
    this.textDirection,
    required this.verticalDirection,
  }) : super(numSupportedChildren: kNumSupportedChildren);

  static const kNumSupportedChildren = -1;

  static const type = 'column';

  final CrossAxisAlignment crossAxisAlignment;
  final MainAxisAlignment mainAxisAlignment;
  final MainAxisSize mainAxisSize;
  final TextBaseline? textBaseline;
  final TextDirection? textDirection;
  final VerticalDirection verticalDirection;

  static JsonColumnBuilder? fromDynamic(
    dynamic map, {
    JsonWidgetRegistry? registry,
  }) {
    JsonColumnBuilder? result;

    if (map != null) {
      result = JsonColumnBuilder(
        crossAxisAlignment: ThemeDecoder.decodeCrossAxisAlignment(
              map['crossAxisAlignment'],
              validate: false,
            ) ??
            CrossAxisAlignment.center,
        mainAxisAlignment: ThemeDecoder.decodeMainAxisAlignment(
              map['mainAxisAlignment'],
              validate: false,
            ) ??
            MainAxisAlignment.start,
        mainAxisSize: ThemeDecoder.decodeMainAxisSize(
              map['mainAxisSize'],
              validate: false,
            ) ??
            MainAxisSize.max,
        textBaseline: ThemeDecoder.decodeTextBaseline(
          map['textBaseline'],
          validate: false,
        ),
        textDirection: ThemeDecoder.decodeTextDirection(
          map['textDirection'],
          validate: false,
        ),
        verticalDirection: ThemeDecoder.decodeVerticalDirection(
              map['verticalDirection'],
              validate: false,
            ) ??
            VerticalDirection.down,
      );
    }

    return result;
  }

  @override
  Widget buildCustom({
    ChildWidgetBuilder? childBuilder,
    required BuildContext context,
    required JsonWidgetData data,
    Key? key,
  }) {
    return Column(
      crossAxisAlignment: crossAxisAlignment,
      key: key,
      mainAxisAlignment: mainAxisAlignment,
      mainAxisSize: mainAxisSize,
      textBaseline: textBaseline,
      textDirection: textDirection,
      verticalDirection: verticalDirection,
      children: [
        for (var child in data.children ?? <JsonWidgetData>[])
          child.build(
            context: context,
            childBuilder: childBuilder,
          ),
      ],
    );
  }
}

class ColumnSchema {
  static const id =
      'https://peiffer-innovations.github.io/flutter_json_schemas'
        '/schemas/json_dynamic_widget/column.json';

  static final schema = {
    r'$schema': 'http://json-schema.org/draft-06/schema#',
    r'$id': id,
    r'$children': -1,
    r'$comment': 'https://api.flutter.dev/flutter/widgets/Column-class.html',
    'title': 'Column',
    'oneOf': [
      {
        'type': 'null',
      },
      {
        'type': 'object',
        'additionalProperties': false,
        'properties': {
          'crossAxisAlignment':
              SchemaHelper.objectSchema(CrossAxisAlignmentSchema.id),
          'mainAxisAlignment':
              SchemaHelper.objectSchema(MainAxisAlignmentSchema.id),
          'mainAxisSize': SchemaHelper.objectSchema(MainAxisSizeSchema.id),
          'textBaseline': SchemaHelper.objectSchema(TextBaselineSchema.id),
          'textDirection': SchemaHelper.objectSchema(TextDirectionSchema.id),
          'verticalDirection':
              SchemaHelper.objectSchema(VerticalDirectionSchema.id),
        },
      },
    ],
  };
}

Migration CLI

要迁移现有JSON/YAML文件,请首先将包添加为依赖项:

dependencies:
  json_dynamic_widget: <version>

然后运行:

dart run json_dynamic_widget:migrate_7 [path/to/files]

Usage

由于该库允许动态构建图标,Flutter内置的图标树抖动功能不再能保证哪些图标被引用。因此,必须添加 --no-tree-shake-icons 标志进行构建。

flutter build [apk | web | ios | ...] --no-tree-shake-icons

JSON结构示例

{
  "type": "<lower_case_type>",
  "id": "<optional-id>",
  "listen": [
    "var1",
    "var2",
    "..."
  ],
  "args": {
    "...": "..."
  },
}

YAML结构示例

---
type: <lower_case_type>
id: <optional-id>
listen: 
  - var1
  - var2
  - ...
args:
  ...: ...

示例代码

import 'package:flutter/material.dart';
import 'package:json_dynamic_widget/json_dynamic_widget.dart';

class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({
    @required this.jsonData,
    this.registry,
    Key key,
  }): assert(jsonData != null),
    super(key: key)

  final Map<String, dynamic> jsonData;
  final JsonWidgetRegistry registry;

  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyStatefulWidget> {
  late JsonWidgetData _data;

  @override
  void initState() {
    super.initState();

    _data = JsonWidgetData.fromDynamic(widget.jsonData);
  }

  @override
  Widget build(BuildContext context) => _data.build(
    context, 
    registry: widget.registry ?? JsonWidgetRegistry.instance,
  );
}

Understanding the Registry

JsonWidgetRegistry 是集中处理JSON动态小部件的中心仓库。它负责注册小部件、提供动态变量和函数,以及处理参数处理器。

Built In Widgets

所有内部构造器默认都会添加到注册表中。例如,align 构造器可以在 align.json 中找到示例。

Using Expressions

自4.0.0版本以来,该库与 expressions 库紧密集成,可以在JSON中定义表达式。

使用变量

变量可以在用于渲染动态小部件的 JsonWidgetRegistry 中定义。例如:

${dynamicVariable}
${dynamic['persons'][0]}
${'Hello ' + name}

动态函数

基本用法

${sayHello('Hello,' + firstName)}

命名参数

${myFunction({'key':'keyName', 'value':value})}

复杂调用

${func1(func2(func3()+' text'+var1), func4(1+2))}

Creating Custom Widgets

创建自定义小部件需要首先创建一个 JsonWidgetBuilder 。例如,创建一个SVG渲染小部件:

import 'package:flutter_svg/flutter_svg.dart';
import 'package:json_dynamic_widget/json_dynamic_widget.dart';

part 'svg_builder.g.dart';

@jsonWidget
abstract class _SvgBuilder extends JsonWidgetBuilder {
  const _SvgBuilder({
    required super.args,
  });

  @override
  _Svg buildCustom({
    ChildWidgetBuilder? childBuilder,
    required BuildContext context,
    required JsonWidgetData data,
    Key? key,
  });
}

class _Svg extends StatelessWidget {
  const _Svg({
    this.asset,
    this.color,
    this.height,
    this.url,
    this.width,
  })  : assert(asset == null || url == null),
        assert(asset != null || url != null);

  final String? asset;
  final Color? color;
  final double? height;
  final String? url;
  final double? width;

  @override
  Widget build(BuildContext context) {
    return asset != null
        ? SvgPicture.asset(
            asset!,
            height: height,
            width: width,
          )
        : SvgPicture.network(
            url!,
            height: height,
            width: width,
          );
  }
}

然后运行代码生成命令:

dart run build_runner build --delete-conflicting-outputs

最后,使用以下JSON构造实例:

{
  "type": "svg",
  "args": {
    "asset": "assets/images/visa.svg",
    "color": "#fff",
    "height": 40,
    "width": 56
  }
}

Creating Custom Arg Processor

为了扩展JSON语法,可以创建自定义参数处理器。例如,将 "TRUE""FALSE" 转换为布尔值:

import 'package:json_dynamic_widget/json_dynamic_widget.dart';

class BooleanStringArgProcessor implements ArgProcessor {
  final _matchRegexp = RegExp(r'^TRUE|FALSE$');

  @override
  bool support(dynamic arg) {
    return arg != null && arg is String && _matchRegexp.hasMatch(arg);
  }

  @override
  ProcessedArg process(
      JsonWidgetRegistry registry, dynamic arg, Set<String>? listenVariables) {
    var resultListenVariables = listenVariables ?? <String>{};
    var boolStr = _matchRegexp.firstMatch(arg)!.toString();
    return ProcessedArg(
        listenVariables: resultListenVariables,
        value: boolStr == 'TRUE',
    );
  }
}

var registry = JsonWidgetRegistry.instance;

registry.registerArgProcessors(
   <ArgProcessor>[BooleanStringArgProcessor()].addAll(ArgProcessors.defaults)
);

以上是 json_dynamic_widget 插件的主要内容和使用方法。希望对你有所帮助!


更多关于Flutter动态JSON解析生成UI插件json_dynamic_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动态JSON解析生成UI插件json_dynamic_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 json_dynamic_widget 插件在 Flutter 中动态解析 JSON 并生成 UI 的代码示例。json_dynamic_widget 是一个强大的 Flutter 插件,它允许你根据 JSON 数据动态地构建用户界面。

首先,确保你已经在 pubspec.yaml 文件中添加了 json_dynamic_widget 依赖:

dependencies:
  flutter:
    sdk: flutter
  json_dynamic_widget: ^x.y.z  # 请使用最新版本号

然后,你可以使用以下步骤来实现动态 JSON 解析和 UI 生成。

1. 导入必要的包

在你的 Dart 文件中导入 json_dynamic_widget 及其相关包:

import 'package:flutter/material.dart';
import 'package:json_dynamic_widget/json_dynamic_widget.dart';
import 'package:json_dynamic_widget_plugin_flutter/json_dynamic_widget_plugin_flutter.dart';

2. 注册插件

MaterialAppCupertinoAppbuilder 函数中注册 json_dynamic_widget 插件:

void main() {
  JsonDynamicWidgetPluginFlutter().registerWith(
    registry: Registrar()
  );

  runApp(MyApp());
}

3. 创建一个 JSON 数据示例

假设你有一个 JSON 数据,如下所示:

{
  "type": "Column",
  "children": [
    {
      "type": "Text",
      "text": "Hello, World!"
    },
    {
      "type": "Button",
      "text": "Click Me",
      "onPressed": {
        "type": "Action",
        "action": "showSnackbar",
        "args": {
          "message": "Button clicked!"
        }
      }
    }
  ]
}

4. 动态解析 JSON 并生成 UI

在你的 Flutter 组件中,使用 JsonDynamicWidgetBuilder 来解析 JSON 并生成 UI:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // JSON 数据
    String jsonData = '''
    {
      "type": "Column",
      "children": [
        {
          "type": "Text",
          "text": "Hello, World!"
        },
        {
          "type": "Button",
          "text": "Click Me",
          "onPressed": {
            "type": "Action",
            "action": "showSnackbar",
            "args": {
              "message": "Button clicked!"
            }
          }
        }
      ]
    }
    ''';

    // 将 JSON 数据解析为 Map
    final jsonMap = jsonDecode(jsonData);

    // 使用 JsonDynamicWidgetBuilder 构建 UI
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Dynamic JSON UI'),
        ),
        body: JsonDynamicWidgetBuilder.build(jsonMap),
      ),
    );
  }
}

5. 处理自定义动作(可选)

如果你需要在按钮点击时执行自定义动作(如显示 Snackbar),你需要扩展 JsonDynamicWidgetPlugin 来处理这些动作。以下是一个简单的示例:

class CustomActionPlugin extends JsonDynamicWidgetPlugin {
  @override
  List<String> getTypes() => ['Action'];

  @override
  Widget build(
    BuildContext context,
    Map<String, dynamic> jsonMap,
    JsonDynamicWidgetBuilder? builder,
    Widget? child,
  ) {
    if (jsonMap['action'] == 'showSnackbar') {
      return ElevatedButton(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackbar(
            SnackBar(
              content: Text(jsonMap['args']['message']),
            ),
          );
        },
        child: Text(jsonMap['text'] ?? 'Button'),
      );
    }
    return super.build(context, jsonMap, builder, child);
  }
}

然后在 main 函数中注册这个插件:

void main() {
  JsonDynamicWidgetPluginRegistry.instance
    ..registerPlugin(CustomActionPlugin())
    ..registerDefaultPlugins();

  JsonDynamicWidgetPluginFlutter().registerWith(
    registry: Registrar()
  );

  runApp(MyApp());
}

现在,当你点击按钮时,应该会显示一个 Snackbar。

通过这些步骤,你可以使用 json_dynamic_widget 插件在 Flutter 中动态解析 JSON 并生成 UI。如果你有更复杂的 JSON 结构或需要更多的自定义行为,你可以根据需要扩展和修改这些代码。

回到顶部