Coder Social home page Coder Social logo

dengyin2000 / dynamic_widget Goto Github PK

View Code? Open in Web Editor NEW
1.5K 40.0 306.0 22.07 MB

A Backend-Driven UI toolkit, build your dynamic UI with json, and the json format is very similar with flutter widget code.

License: Apache License 2.0

Java 0.18% Dart 99.31% Kotlin 0.08% Objective-C 0.25% Swift 0.13% HTML 0.06%
flutter widget dynamicwidget dynamic

dynamic_widget's Introduction

Pub  Awesome Flutter

Flutter Dynamic Widget

A Backend-Driven UI toolkit, build your dynamic UI with json, and the json format is very similar with flutter widget code.

From 4.0.0-nullsafety.1 version, it supports null-safety.

From 3.0.0 version, it supports exporting your flutter code to json code. please check How to write the json code

From 1.0.4 version, it supports flutter web application.

Table of contents

General info

I work for an e-commerce company. We need to build flexible pages. So we define a light UI protocol, and implement on Android and iOS. We can dynamic update App UIs by pushing a json file. With this ability, we can do some UI A/B testing without publishing App to app store. Flutter allows you to build beautiful native apps on iOS and Android from a single codebase, it can allow you to build web app later. Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bugs faster. But it still build native app, the UIs can't be dynamic updated. If you want to modify the UIs, you need to publish the updated app to app store. With this project, you can build your UIs from a json string, which is the UI protocol. The json string is very similar with the Flutter widget dart code. All widget type and widget properties are the same.

Widget type will be a type property, and widget's properties will be the json properties too. All properties and their values will be almost the same. You can checkout the following document.

Currently support flutter widgets and properties

Screenshots

Install

1. Depend on it

Add this to your package's pubspec.yaml file:

dependencies:
  dynamic_widget: ^3.0.3

2. Install it

You can install packages from the command line:

with Flutter:

$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:dynamic_widget/dynamic_widget.dart';

Get started

You should use DynamicWidgetBuilder().build method to covert a json string into flutter widget. It will be time-consuming. so you'd better using FutureBuilder to build the UI.

import 'package:dynamic_widget/dynamic_widget.dart';
class PreviewPage extends StatelessWidget {
  final String jsonString;

  PreviewPage(this.jsonString);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text("Preview"),
      ),
      body: FutureBuilder<Widget>(
        future: _buildWidget(context),
        builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
          if (snapshot.hasError) {
            print(snapshot.error);
          }
          return snapshot.hasData
              ? SizedBox.expand(
                  child: snapshot.data,
                )
              : Text("Loading...");
        },
      ),
    );
  }

  Future<Widget> _buildWidget(BuildContext context) async {
    return DynamicWidgetBuilder.build(jsonString, context, new DefaultClickListener());
  }
}

How to implement a WidgetParser

  1. You need to implement the WidgetParser abstract class.
  2. Add new created WidgetParser by DynamicWidgetBuilder.addParser(WidgetParser parser) method.

This is a RaisedButton widget parser.

import 'package:dynamic_widget/dynamic_widget/utils.dart';
import 'package:dynamic_widget/dynamic_widget.dart';
import 'package:flutter/material.dart';

class RaisedButtonParser extends WidgetParser {
  @override
  String get widgetName => "RaisedButton";

  @override
  Widget parse(Map<String, dynamic> map, BuildContext buildContext, ClickListener listener) {
    String clickEvent =
        map.containsKey("click_event") ? map['click_event'] : "";

    var raisedButton = RaisedButton(
      color: map.containsKey('color') ? parseHexColor(map['color']) : null,
      disabledColor: map.containsKey('disabledColor')
          ? parseHexColor(map['disabledColor'])
          : null,
      disabledElevation:
          map.containsKey('disabledElevation') ? map['disabledElevation']?.toDouble() : 0.0,
      disabledTextColor: map.containsKey('disabledTextColor')
          ? parseHexColor(map['disabledTextColor'])
          : null,
      elevation: map.containsKey('elevation') ? map['elevation']?.toDouble() : 0.0,
      padding: map.containsKey('padding')
          ? parseEdgeInsetsGeometry(map['padding'])
          : null,
      splashColor: map.containsKey('splashColor')
          ? parseHexColor(map['splashColor'])
          : null,
      textColor:
          map.containsKey('textColor') ? parseHexColor(map['textColor']) : null,
      child: DynamicWidgetBuilder.buildFromMap(map['child'], buildContext, listener),
      onPressed: () {
        listener.onClicked(clickEvent);
      },
    );

    return raisedButton;
  }
}

Add it to parsers list.

DynamicWidgetBuilder.addParser(RaisedButtonParser());

How to add a click listener

Add "click_event" property to your widget json definition. for example:

var raisedButton_json =
'''
{
  "type": "Container",
  "alignment": "center",
  "child": {
    "type": "RaisedButton",
    "color": "##FF00FF",
    "padding": "8,8,8,8",
    "textColor": "#00FF00",
    "elevation" : 8.0,
    "splashColor" : "#00FF00",
    "click_event" : "route://productDetail?goods_id=123",
    "child" : {
      "type": "Text",
      "data": "I am a button"
    }
  }
}

We suggest you'd better to use an URI to define the event, as the exmaple, it's a event for going to a product detail page.

Then, define a ClickListener

class DefaultClickListener implements ClickListener{
  @override
  void onClicked(String event) {
    print("Receive click event: " + event);
  }

}

Finally, pass the listener to build method.

  Future<Widget> _buildWidget() async{

    return DynamicWidgetBuilder.build(jsonString, buildContext, new DefaultClickListener());
  }

How to write the json code

You don't need to write the json code by hand, you can export your flutter code to json code efficiently with DynamicWidgetJsonExportor widget. You just need to wrap your flutter code with DynamicWidgetJsonExportor widget, then invoke its exportJsonString() method, look at following example, click the "export" button, it will find the DynamicWidgetJsonExportor widget, and export its child to json code efficiently.

class _JSONExporterState extends State<JSONExporter> {
  GlobalKey key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text("export example"),
      ),
      body: Builder(
        builder: (context) => Column(
          children: [
            Expanded(
              child: DynamicWidgetJsonExportor(
                key: key,
                child: Container(
                  child: GridViewWidget(
                      GridViewParams(
                          mainAxisSpacing: 2.0,
                          crossAxisSpacing: 2.0,
                          crossAxisCount: 2,
                          childAspectRatio: 1.6,
                          padding: EdgeInsets.all(10.0),
                          pageSize: 10,
                          children: [
                            ListTile(
                              leading: Text("Leading text"),
                              title: Text("title"),
                              subtitle: Text("subtitle"),
                            ),
                            ListTile(
                              leading: Text("Leading text"),
                              title: Text("title"),
                              subtitle: Text("subtitle"),
                            )
                          ]),
                      context),
                ),
              ),
            ),
            RaisedButton(
              child: Text("Export"),
              onPressed: () {
                var exportor = key.currentWidget as DynamicWidgetJsonExportor;
                var exportJsonString = exportor.exportJsonString();
                Scaffold.of(context).showSnackBar(SnackBar(
                    content: Text("json string was exported to editor page.")));
                Future.delayed(Duration(seconds: 3), (){
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) =>
                              CodeEditorPage(exportJsonString)));
                });
              },
            )
          ],
        ),
      ),
    );
  }
}

You can use whatever your favorite IDE to build the UI, then use DynamicWidgetJsonExportor to export to json code. For detail, please check the Dynamic Widget Demo source code.

Widget Documents

Already completed widgets:

You can view Currently support widgets and properties here.

Setup

Checkout this project and run demo.

Code Examples

Checkout this project and run demo.

Contact

Created by @[email protected] - feel free to contact me

dynamic_widget's People

Contributors

agostinofiscale avatar alekseichuk avatar alsofr avatar bhtri avatar changjoo-park avatar dengyin2000 avatar fgh151 avatar gabrielferreir avatar michealreed avatar mrjacobphillips avatar nathanael540 avatar revitate avatar sis-tangyuan avatar tvolkert avatar vanyasem avatar zhangruiyu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dynamic_widget's Issues

[Question] How to add multiple views in single page?

Firstly Thank you very much for the Library. This is so helpful.

Question:
I want to add few views dynamically in a single page. How do I need to define in the JSON and how to parse that data to your library?

Callback

Thank you for the great plugin,
Is this plugin support callback event from child to parent widget?
if it does not support, do you have any ideas on how to implement it?

Directions should not be LTR

When using RTL app and not specifying any text/layout direction the library's default is LTR.
I think it would be better if none specified by the JSON, than let the system decide the right direction ("start" direction for example).

Does it support Bloc?

I just need to know, this dynamic widget can support of this format?

onPressed: () { BlocProvider.of<HomeBloc>(context) .add(StartBackgroundLocation()); })

Please let me know is this possible to convert the above. Thanks

有几个问题想问一下

看了咸鱼的Flutter动态化探索的文章,了解到这个库的,初学Flutter想问下这个动态更新Wiget的库,支持listview动态加载数据吗?listview是那种需要分页加载数据的,能否通过动态更新widget的方式实现listview分页加载的效果呢?还是说这种方式只能更新静态的界面

onClicked should pass dynamic

I'm starting to use your library which is awesome and help me a lot, and I've been thinking that maybe onClicked in ClickListener should pass 'dynamic' rather than 'String' so we could pass custom objects or even map.
Thanks!

showcase

Is there a blog post or live app on the stores that use this, or live examples from the network?

This just really seems like it shows a lot of promise re: code push competition?
Thanks

is it a dynamic DSL solution?

Hi, Dengyin.

Thanks for shared good project.

Has one question.

Does this have any relevance to JS? In other words, is it a dynamic DSL solution?

infinity value error

image
image

999999999.99 : this double value length is 9, but infinity value length is 10, This judgment will never hold

Bug:params error

I use the example like this:
image

but when i open your source code ,it doesn't have the context params:
image

something wrong and i don't know why if i do not pass the context param

Translation Support

Hi, thanks for sharing such a useful library.

For i18n, how do we support translation from JSON data?

Thanks in advance.

LICENSE needed

@dengyin2000

Hi, great work!

I was thinking of creating a similar library, but hey, you already implemented most of what I had in mind. Is there a LICENSE for this library?

Business Logic Dynamic

Hi, @dengyin2000
Congratulations for dynamic_widget! It´s a very useful package.
I need to do something like this for BL layer too. For example: My app has a default business logic for sales commission, but each of my clients wants to create their own rule. I would like to be able to inject this custom code according to the user who has logged in my app.
Would you have any idea how to meet this scenario on Flutter?

Thanks

Leandro

Feature proposal - using an already parsed json

First - Thank you for the library.

A feature proposal: In my use case I already have a parsed json (using for my own widgets) and it will be helpful to have a method that will accept a map (not a jsonString) if it is already available in runtime.

handle button click

It is possible to button click to change clicked button background color or add new widget on button click.
Something like in wrap example to 7 button and I click on 3rd button then change background color of 3rd button then I click 5th button then set background color on 5th button and 3rd button to remove background and set as default
If it's possible then please help me.
Thank you

Missing FontAwesomeIcons.adobe

This Icon has been deleted from FontAwesome

https://fontawesome.com/icons/adobe

|-- dynamic_widget 2.0.3
|   |-- flutter...
|   |-- font_awesome_flutter 8.9.0



https://github.com/FortAwesome/Font-Awesome/blob/57005cea6da7d1c67f3466974aecd25485f60452/UPGRADING.md#5140-to-5150

## 5.14.0 to 5.15.0
--
18 |  
19 | The adobe icon has been removed by legal request of Adobe.
20 |  
21 | Font Awesome is no longer able to provide any logos or marks for the Adobe


Workaround:

Use this in pubspec.yaml

dependency_overrides:
  font_awesome_flutter: 8.8.1 # https://pub.dev/packages/font_awesome_flutter

Fails to compile on latest master branch

After running flutter upgrade, I am now seeing this error when attempting to compile.

Target dart2js failed: Exception: /C:/Users/user_folder/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/dynamic_widget-2.0.3/lib/dynamic_widget/utils.dart:496:1:
Error: Type 'Overflow' not found.
Overflow parseOverflow(String value) {
^^^^^^^^
/C:/Users/user_folder/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/dynamic_widget-2.0.3/lib/dynamic_widget/basic/stack_positioned_widgets_parser.dart:37:7:
Error: No named parameter with the name 'overflow'.
      overflow: map.containsKey("overflow")
      ^^^^^^^^
/C:/src/flutter/packages/flutter/lib/src/widgets/basic.dart:3273:3:
Info: Found this candidate, but the arguments don't match.
  Stack({
  ^^^^^
/C:/Users/user_folder/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/dynamic_widget-2.0.3/lib/dynamic_widget/utils.dart:503:14:
Error: Getter not found: 'Overflow'.
      return Overflow.visible;
             ^^^^^^^^
/C:/Users/user_folder/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/dynamic_widget-2.0.3/lib/dynamic_widget/utils.dart:505:14:
Error: Getter not found: 'Overflow'.
      return Overflow.clip;
             ^^^^^^^^
/C:/Users/user_folder/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/dynamic_widget-2.0.3/lib/dynamic_widget/utils.dart:507:14:
Error: Getter not found: 'Overflow'.
      return Overflow.clip;
             ^^^^^^^^
Error: Compilation failed.

Goto Page on click event

Hello, i am using click event, but unable to understand how can i open specific page by check route params ?

route://userDetails?userid=123

Want to goto user details page and read 123

click问题

void onClicked(String event)

上面的clicked方法中,参数context都没有,后续怎样通过Navigator.of(context)方法跳转?

[Spelling] Protocol is spelled "protocal" in readme.

With this project, you can build your UIs from a json string, which is the UI protocal. The json string is very similar with the Flutter widget dart code. All widget type and widget properties are the same.

Installation error on "pub get failed (65; ╵)"

`dependencies:
flutter:
sdk: flutter
dynamic_widget: ^0.0.3
cupertino_icons: ^0.1.2

dev_dependencies:
flutter_test:
sdk: flutter
`

I am not sure where I did wrong but am I wrong to put it inside pubspec.yaml?
I got an error as following:

`Running "flutter pub get" in dynamic_widget...
Error on line 23, column 3 of pubspec.yaml: A package may not list itself as a dependency.

23 │ dynamic_widget: ^0.0.3

│ ^^^^^^^^^^^^^^


pub get failed (65; ╵)
Process finished with exit code 65`

editable forms - click event and logic

I would like to do a post back and collect form data in the widget.

SO this means databinding from the value of the widget to a JSON or name value array and then adding that to the URL as a payload.

this would allow editable forms

suggestion: Make buildFromMap accept dynamic instead of Map

change
static Widget buildFromMap(Map<String, dynamic> map,
BuildContext buildContext, ClickListener listener) {
to
static Widget buildFromMap(dynamic map,
BuildContext buildContext, ClickListener listener) {
if(map is String)
return Text(map);

if((map==null) || !map.containsKey('type'))
  return null;

so the meta data can be more simple, more error tolerable, support setting like

{ "type":"Padding", "padding":"0,4,0,0" }

{ "type":"Padding", "padding":"0,4,0,0", "child":"test" }

Flare-Flutter Support ! [enhancement]

it will be great if we get the Flare support

Widget build(BuildContext context) {
    return new FlareActor("animations/boring_star.flr",
        alignment: Alignment.center,
        fit: BoxFit.contain,
        animation: "rotate_scale_color");
  }

ListView with scrollDirection horizontal not working

Hi, im having issue in rendering ListView with horizontal scroll direction. I want to create a scrollable horizontal listview. Am i missing out some settings?

Thank you

This is my JSON:

{ "type": "Container", "alignment": "center", "child": { "type": "ListView", "scrollDirection": "horizontal", "margin": "8,8,8,8", "children" : [{ "type": "Container", "color":"#FF00FF", "width": 500.0, "height": 50, "child":{ "type": "Text", "data":"kimmy1", "textAlign":"center" } }, { "type": "Container", "color":"#FF00FF", "width": 500.0, "height": 50, "child":{ "type": "Text", "data":"kimmyB", "textAlign":"center" } }, { "type": "Container", "color":"#FF00FF", "width": 500.0, "height": 50, "child":{ "type": "Text", "data":"kimmy3", "textAlign":"center" } }] } }

請教關於ListView的loadMoreUrl

第一次ListVist產生沒有問題, 但加載更多的時候, 只需要將loadMoreUrl參數帶上, 但是要如何變動loadMoreUrl參數?
例如第一次加載10筆, loadMoreUrl 也指定了第10-20的url, 那第20-30筆怎進行?

A few suggestions

Good job. When I use it, I fell the below features are necessary.

1, A way to add custom parsers. I use listener to do it.
2, A way to pass more objects, for example, pass the context into the parsers. I am using the listener to do it now
3, Pass extra parameters to click event

Navigate routes

How do I page detail a click_event route

Navigator.pushNamed(context, '/second');

type 'int' is not a subtype of type 'double'

When minifying a JSON. any double without decimal are converted into int type

Original

    {
      "type": "SizedBox",
      "height": 19.0,
      "child": {
        "type": "Container",
        "color": "#FFFFFF"
      }
    },

minified

{"type":"SizedBox","height":19,"child":{"type":"Container","color":"#FFFFFF"}}

https://codebeautify.org/jsonminifier

Error when building above JSON

The following _TypeError was thrown building BlocBuilder<Cubit, String>
type 'int' is not a subtype of type 'double'

The relevant error-causing widget was: 
  BlocBuilder<Cubit, String>home_page.dart:
When the exception was thrown, this was the stack: 
#0      SizedBoxWidgetParser.parse (package:dynamic_widget/dynamic_widget/basic/sizedbox_widget_parser.dart:34:18)
#1      DynamicWidgetBuilder.buildFromMap (package:dynamic_widget/dynamic_widget.dart:116:21)
#2      DynamicWidgetBuilder.buildWidgets (package:dynamic_widget/dynamic_widget.dart:127:14)
#3      ListViewWidgetParser.parse (package:dynamic_widget/dynamic_widget/scrolling/listview_widget_parser.dart:27:41)
#4      DynamicWidgetBuilder.buildFromMap (package:dynamic_widget/dynamic_widget.dart:116:21)

There is a PR #48 which solve this issue on some Parser. Still missing the fix in SizedBox parser though.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.