GitHub issues GitHub forks GitHub stars GitHub license Pub Likes Pub Points Popularity style: effective dart GitHub tag (latest SemVer) GitHub top language GitHub contributors GitHub last commit Flutter CI

Convert JSON to Widget validating with JSON Schema for Flutter apps

What is it

The schema_widget is a Flutter package implemented with base on dynamic_widget package that produces widgets dynamically interpreting JSON objects.

Author

Writed by Alex Manoel Ferreira Silva Support - Recommend/Endorse me on LinkedIn

Under Legytma Soluções Inteligentes Company - Legytma Soluções Inteligentes in LinkedIn

Motivation

A major difficulty for any application developer is to ensure that all users keep their applications up to date to ensure the same user experience and to reduce the time required to fix bugs.

The most commonly used alternative to accelerate this process is Code Push which allows the application update without the need for a new deploy in the store. However in Code Push GitHub itself there is a Code Push Support for Flutter request with comment saying that support depends on implementing dynamic update support in Flutter, there is also a reference to Flutter Roadmap saying that support for This type of update is postponed according to the official comment Code Push / Hot Update / out of band updates, which explains the reasons that led to the decision.

One possible solution to this deadlock is to implement a JSON interpreter that enables screen redesign, which can be obtained using dynamic_widget. Although dynamic_widget provides an easy way to extend support for other widgets through separate implementations. It limits the use of events and does not allow the use of complex business rules.

How this work

The basic operation of schema_widget is very similar to that of dynamic_widget. Both interpret JSON and instantiate the widget for it. The main difference with schema_widget is that it allows the creation of parsers for any type of object and the use of pre-implemented rules. For this reason, it makes it possible to create much more complex business rules.

The schema_widget package provides the SchemaWidget class, in most cases this will be the only class used. All methods in this class are static.

To add a new parser, the following methods can be used:

Default parsers: This method loads all the default parsers from schema_widget.

  SchemaWidget.registerParsers();

Synchronous: This method can be used to load a custom parser that can be loaded synchronously.

  TypeSchemaParser typeSchemaParserInstance = CustomTypeSchemaParser();

  SchemaWidget.registerTypeParser(typeSchemaParserInstance);

Asynchronous: This method can be used to load a custom parser that needs to be loaded asynchronously.

  String typeName = "Custom";
  Future<TypeSchemaParser> futureTypeSchemaParserInstance = () async => CustomTypeSchemaParser();

  SchemaWidget.registerTypeParserAsync(typeName, futureTypeSchemaParserInstance);

In addition to parsers, any type of function and object for use by parsers can also be registered. For this, the SchemaWidget.registerLogic(logicName, logic) method must be used. The logic name must be unique, otherwise the previously registered logic will be replaced.

Example:

  const string1 = "Hello World";
  const map1 = {"foo": "bar"};
  const object1 = Foo();
  var func1 = () => "Hello World";
  var asyncFunc1 = () async => "Hello World";
  var object2 = Bar();

  SchemaWidget.registerLogic("string1", string1);
  SchemaWidget.registerLogic("map1", map1);
  SchemaWidget.registerLogic("object1", object1);
  SchemaWidget.registerLogic("func1", func1);
  SchemaWidget.registerLogic("asyncFunc1", asyncFunc1);
  SchemaWidget.registerLogic("otherExemple", object2);

Both parsers and logics are registered using the get_it package to manage everything. After registering all the parsers and logic necessary for use. It may be necessary to wait for GetIt to finish loading asynchronous parsers. For this, GetIt makes GetIt.I.allReady() available for use in a FutureBuilder for the purpose of creating widgets only after all parsers are loaded as shown in the following example.

class MyApp extends StatelessWidget {
  MyApp({Key key}) : super(key: key) {
    SchemaWidget.registerParsers();
    SchemaWidget.registerLogic("funcHelloWorld", () => Text("Hello World"));
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: GetIt.I.allReady(ignorePendingAsyncCreation: false),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (!snapshot.hasData) {
          return CircularProgressIndicator();
        }

        return SchemaWidget.parse<Widget>(
          context,
          {
            "type": "Scaffold",
            "appBar": {
              "type": "AppBar",
              "title": {
                "type": "Text",
                "data": "Hello World Example",
              },
            },
            "body": {
              "type": "Center",
              "child": "funcHelloWorld",
            },
          },
        );
      },
    );
  }
}

Alternatively, SchemaWidget itself can be used to execute SchemaWidget.registerParsers() and SchemaWidget.parse<Widget>(), reducing the complexity of the code.

class MyApp extends StatelessWidget {
  MyApp({Key key}) : super(key: key) {
    SchemaWidget.registerLogic("funcHelloWorld", () => Text("Hello World"));
  }

  @override
  Widget build(BuildContext context) {
    return SchemaWidget(
      {
        "type": "Scaffold",
        "appBar": {
          "type": "AppBar",
          "title": {
            "type": "Text",
            "data": "Hello World Example",
          },
        },
        "body": {
          "type": "Center",
          "child": "funcHelloWorld",
        },
      },
    );
  }
}

TypeSchemaParser

Everything in schema_widget is based on TypeSchemaParser.

TypeSchemaParser is the implementation of a JSON interpreter that converts it into a complex object.

Getting Started

  • For help over SchemaWidget usage, view the example;
  • For help over class documentation, view the documentation;
  • For help getting started with Flutter, view our online documentation;
  • For help on editing package code, view the documentation.

Installation

  • Add this to your package's pubspec.yaml file:
dependencies:
  get_it:
  schema_widget: ^1.0.0
  • Install packages from the command line: with Flutter:
$ flutter packages get
  • Import it into the code file:
 import 'package:schema_widget/schema_widget.dart';

Usage

import 'dart:async';

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();

  MyApp({Key key}) : super(key: key) {
    SchemaWidget.registerLogic(
      "onGenerateRoute",
      _onGenerateRoute,
    );
    SchemaWidget.registerLogic(
      "onUnknownRoute",
      _onUnknownRoute,
    );
    SchemaWidget.registerLogic(
      "navigatorKey",
      _navigatorKey,
    );
  }

  @override
  Widget build(BuildContext context) {
    return SchemaWidget(
      loadSchemas: false,
      {
        "type": "MaterialApp",
        "title": 'SchemaWidget Demo',
        "theme": {
          "primarySwatch": {
            "primary": 0xFF2196F3,
            "swatch": {
              "50": 0xFFE3F2FD,
              "100": 0xFFBBDEFB,
              "200": 0xFF90CAF9,
              "300": 0xFF64B5F6,
              "400": 0xFF42A5F5,
              "500": 0xFF2196F3,
              "600": 0xFF1E88E5,
              "700": 0xFF1976D2,
              "800": 0xFF1565C0,
              "900": 0xFF0D47A1,
            },
          },
        },
        "navigatorKey": "navigatorKey",
        "initialRoute": "home",
        "onGenerateRoute": "onGenerateRoute",
        "onUnknownRoute": "onUnknownRoute"
      },
    );
  }

  Route _onGenerateRoute(RouteSettings settings) {
    return MaterialPageRoute(
      builder: (buildContext) => MyHomePage(),
      settings: settings,
    );
  }

  Route _onUnknownRoute(RouteSettings settings) {
    return MaterialPageRoute(
      builder: (buildContext) => MyHomePage(),
      settings: settings.copyWith(name: "home"),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key}) : super(key: key) {
    SchemaWidget.registerLogic("funcHelloWorld", () => Text("Hello World"));
  }

  @override
  Widget build(BuildContext context) {
    return SchemaWidget(
      {
        "type": "Scaffold",
        "appBar": {
          "type": "AppBar",
          "title": {
            "type": "Text",
            "data": "Hello World Example",
          },
        },
        "body": {
          "type": "Center",
          "child": "funcHelloWorld",
        },
      },
    );
  }
}

Next steps

  • x Publish Package;
  • x Make MVP;
  • x Minimal documentation;
  • x Change event binding of click event;
  • x Add list of default supported Widgets;
  • x Add list of default supported Types;
  • Create content about;
  • Publish complementar packages;
  • Create example full functional apps;
  • Make a commercial product using the package;

Libraries

align_schema_widget_parser
alignment_schema_parser
animated_container_schema_widget_parser
app_bar_schema_widget_parser
app_bar_theme_schema_parser
aspect_ratio_schema_widget_parser
axis_schema_parser
base_cache_manager_json_schema_resolver
baseline_schema_widget_parser
blend_mode_schema_parser
border_radius_geometry_schema_parser
border_radius_schema_parser
border_schema_parser
border_side_schema_parser
border_style_schema_parser
bottom_app_bar_theme_schema_parser
bottom_sheet_theme_data_schema_parser
box_border_schema_parser
box_constraints_schema_parser
box_decoration_schema_parser
box_fit_schema_parser
box_shadow_schema_parser
box_shape_schema_parser
brightness_schema_parser
builder_factory
button_bar_layout_behavior_schema_parser
button_bar_theme_data_schema_parser
button_text_theme_schema_parser
button_theme_data_schema_parser
card_schema_widget_parser
card_theme_schema_parser
center_schema_widget_parser
chip_theme_data_schema_parser
circle_avatar_schema_widget_parser
clip_r_rect_schema_widget_parser
clip_schema_parser
color_filter_schema_parser
color_schema_parser
color_scheme_schema_parser
column_schema_widget_parser
container_schema_widget_parser
cross_axis_alignment_schema_parser
cupertino_text_theme_data_schema_parser
cupertino_theme_data_schema_parser
curve_schema_parser
decoration_image_schema_parser
decoration_schema_parser
dialog_theme_schema_parser
divider_schema_widget_parser
divider_theme_data_schema_parser
double_schema_parser
drag_start_behavior_schema_parser
drawer_header_schema_widget_parser
drawer_schema_widget_parser
duration_schema_parser
edge_insets_geometry_schema_parser
edge_insets_schema_parser
expanded_schema_widget_parser
filter_quality_schema_parser
fitted_box_schema_widget_parser
floating_action_button_animator_schema_parser
floating_action_button_location_schema_parser
floating_action_button_schema_widget_parser
floating_action_button_theme_data_schema_parser
flutter_logo_decoration_schema_parser
flutter_logo_style_schema_parser
focus_node_schema_parser
font_style_schema_parser
font_weight_schema_parser
gesture_detector_schema_widget_parser
grid_view_params
grid_view_params_schema_parser
grid_view_schema_widget_parser
grid_view_widget
grid_view_widget_state
icon_button_schema_widget_parser
icon_data_schema_parser
icon_schema_widget_parser
icon_theme_data_schema_parser
image_provider_dynamic_schema_parser
image_repeat_schema_parser
image_schema_widget_parser
indexed_stack_schema_widget_parser
input_decoration_schema_parser
input_decoration_theme_schema_parser
int_schema_parser
json_schema_resolver
json_schema_schema_parser
list_box_shadow_schema_parser
list_double_schema_parser
list_tile_schema_widget_parser
list_view_params
list_view_params_schema_parser
list_view_schema_widget_parser
list_view_widget
list_view_widget_state
list_widget_schema_parser
local_json_schema_resolver
locale_schema_parser
main_axis_alignment_schema_parser
main_axis_size_schema_parser
material_app_schema_widget_parser
material_banner_theme_data_schema_parser
material_color_schema_parser
material_tap_target_size_schema_parser
network_image_schema_parser
offset_schema_parser
opacity_schema_widget_parser
overflow_schema_parser
padding_schema_widget_parser
page_transitions_theme_schema_parser
page_view_schema_widget_parser
placeholder_schema_widget_parser
positioned_schema_widget_parser
preferred_size_widget_schema_parser
radius_schema_parser
raised_button_schema_widget_parser
rect_schema_parser
route_handle_mixin
row_schema_widget_parser
safe_area_schema_widget_parser
scaffold_schema_widget_parser
schema_parser_annotation
schema_parser_initiator.g
schema_parser_initiator_builder
schema_widget
schema_widget_parser
schema_widget_splash_screen
shape_decoration_schema_parser
show_value_indicator_schema_parser
size_schema_parser
sized_box_schema_widget_parser
slider_theme_data_schema_parser
snack_bar_behavior_schema_parser
snack_bar_theme_data_schema_parser
stack_fit_schema_parser
stack_schema_widget_parser
stream_builder_schema_widget_parser
strut_style_schema_parser
tab_bar_indicator_size_schema_parser
tab_bar_theme_schema_parser
target_platform_schema_parser
text_align_schema_parser
text_align_vertical_schema_parser
text_baseline_schema_parser
text_capitalization_schema_parser
text_decoration_schema_parser
text_decoration_style_schema_parser
text_direction_schema_parser
text_input_action_schema_parser
text_input_type_schema_parser
text_overflow_schema_parser
text_schema_widget_parser
text_span_schema_parser
text_style_schema_parser
text_theme_schema_parser
theme_data_schema_parser
theme_mode_schema_parser
toggle_buttons_theme_data_schema_parser
toolbar_options_schema_parser
tooltip_theme_data_schema_parser
type_schema_parser
type_schema_parser_support_builder
type_schema_parser_test_builder
typography_schema_parser
underline_tab_indicator_schema_parser
utils
variant_schema_widget_parser
variant_type_schema_parser
vertical_direction_schema_parser
widget_schema_parser
wrap_alignment_schema_parser
wrap_cross_alignment_schema_parser
wrap_schema_widget_parser