OndrejKunc / flutter_dynamic_forms

A collection of flutter and dart libraries allowing you to consume complex external forms at runtime.
MIT License
203 stars 59 forks source link

issue in multi insert component #66

Open helmenaem opened 4 years ago

helmenaem commented 4 years ago

I'm trying to add a multi insert component

but changes in the model don't update the UI

attached complete example of the code

Thanks,

example.zip

OndrejKunc commented 4 years ago

Hi @helmenaem ,

I tried your code and I can see a couple of issues:

After you hit add button, you add an item to the list, but itemsChanged Stream doesn't emit value, because it listens to the property changes and not changes to single items in a list.

To fix this you first need to make this list property mutable, so you can change this property later and Stream will emit the value. 1) In your multi_insert_parser.g.dart just add isImmutable: false in the getChildrenProperty method.

2) Your renderer will look like this:

class DefaultMultiInsertRenderer
    extends FormElementRenderer<model.Multiinsert> {
  double iconSize = 40;

  @override
  Widget render(
      model.Multiinsert element,
      BuildContext context,
      FormElementEventDispatcherFunction dispatcher,
      FormElementRendererFunction renderer) {
    return StreamBuilder<List<MultiInsertItem>>(
      initialData: element.items,
      stream: element.itemsChanged,
      builder: (context, snapshot) {
        List<Widget> childrenWidgets = [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              element.name,
              style: TextStyle(color: Colors.grey),
            ),
          )
        ];

        for (var child in element.children) {
          if (child.isVisible) {
            childrenWidgets.add(renderer(child, context));
          }
        }

        childrenWidgets.add(RaisedButton(
            child: Text('Add'),
            onPressed: () {
              var newList = List<MultiInsertItem>.from(
                element.items
                  ..add(
                    MultiInsertItem(
                      rowId: (element.items.isEmpty
                              ? 0
                              : element.items[element.items.length - 1].rowId) +
                          1,
                      rowData: [
                        element.items.length.toString(),
                        'row num : ' + element.items.length.toString()
                      ],
                    ),
                  ),
              );
              var mutableProperty = element.itemsProperty
                  as MutableProperty<List<MultiInsertItem>>;
              mutableProperty.setValue(newList);
              print(element.items);
            }));

        childrenWidgets.add(Table(
            border: TableBorder.all(),
            children: element.items
                .map((entry) => TableRow(
                    children: entry.rowData.map((d) => Text(d)).toList()))
                .toList()));

        return Column(
          children: childrenWidgets,
        );
      },
    );
  }
}

This will update the UI,

Then there is the question of collecting the data back via _formManager.getFormData();. It basically calls toString on all mutable properties so it will call toString on your items property which is a list. You can improve the output by overriding toString in your MultiInsertItem , but to make each MultiInsertItem have one FormItemValue record, you would need to extend FormElement in your MultiInsertItem.

I would recommend checking this issue #65 - specifically CopyContainer in my ok/copy-container branch. It is trying to solve similar problem, but the solution is more general.

helmenaem commented 4 years ago

hello @OndrejKunc,

Thanks for your support, your fixes fixed the re-render of the table but still the check box is having the same issue that it is not evaluating the expression

"isVisible": { "expression": "!@hideWelcomeCheckBox && length(@fullNameLabel) > 0" }

also do you have any implementation of file input ?

sorry again for bothering you I'm new to flutter :(

Thanks,

OndrejKunc commented 4 years ago

Not bothering at all :) I am just busy lately and sometimes my response takes a long time...

The checkbox expression doesn't work because you are not subscribed to the isVisibleChanged streams on the children element... This is the fix - just replace the beginning of your StreamBuilder code with this:

return StreamBuilder(
      stream: MergeStream(
        [
          ...element.children.map((child) => child.isVisibleChanged),
          element.itemsChanged
        ],
      ),

That being said I think it doesn't make much sense to let your Multiinsert component extend container because you need to handle two types of the collections - the children and items. I think Multiinsert should extend just FormElement and care only about the items property.

Unfortunately, I don't have any implementation of file input. I believe the implementation is not that difficult, especially if you keep the content of the file in a string variable. I may try to implement it when I have more time.