Ad

Using Dart And Flutter, How Can I Save Data From A Dynamic Widget, Into A List Of Maps, That Is Inside Of Another List

- 1 answer

apologies if this has already been asked.

Below is the code for a simplified version of an app I'm building.

void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TextEditingController dutyDescriptionController = TextEditingController();

  //this list holds a list of DynamicWidgets()
  List<DynamicWidget> dynamicList = [];

  //this list holds a list of duties
  List<Map<String, dynamic>> dutiesList = [];

  //this list holds a list of activities within a duty, currently just dummy data
  List<Map<String, dynamic>> activitiesList = [
    {'description': 'Some Activity 1'},
    {'description': 'Some Activity 2'},
  ];

  //add DynamicWidget to the dynamicList (max of 3 widgets)
  addDynamic() {
    if (dynamicList.length <= 2) {
      dynamicList.add(DynamicWidget());
      setState(() {});
    }
  }

  //submit the data
  submitData() {
    //there should only be 1 element in the duties list hence why I clear the list prior to adding to it
    //not sure if this is the correct way to go about it
    dutiesList.clear();
    dutiesList.add({
      'dutyDescription': dutyDescriptionController.text,
      'activityDescription': activitiesList,
    });
    print(dutiesList);
    //completely stuck here.
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dynamic Widget'),
      ),
      body: ListView(
        children: [
          TextField(
            controller: dutyDescriptionController,
            decoration: InputDecoration(labelText: 'Duty Description'),
          ),
          Expanded(
            flex: 1,
            child: ListView.builder(
              shrinkWrap: true,
              physics: NeverScrollableScrollPhysics(),
              itemCount: dynamicList.length,
              itemBuilder: (BuildContext context, int index) => dynamicList[index],
            ),
          ),
          ElevatedButton(
            onPressed: addDynamic,
            child: Text('Add Activity'),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          submitData();
        },
        child: Icon(Icons.save),
      ),
    );
  }
}

//this widget is created dynamically and displayed in a listview.builder inside MyApp()
class DynamicWidget extends StatelessWidget {
  final TextEditingController controller1 = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller1,
      decoration: InputDecoration(labelText: 'Activity Description'),
    );
  }
}

The app has a single textfield used to store a duty for a shift of work.

Below that is a button labeled 'Add Activity'. When pressed, it adds a DynamicWidget() to the the dynamicList which is used in conjunction with a listview.builder to display the widget.

There is also a floating action button that when pressed currently adds the contents of the 'Duty Description' textfield to the dutiesList list as well as the contents of activitiesList (which is currently harcoded with dummy data).

This is how the data appears in dutiesList at present. In case it isn't obvious, the key 'activityDescription' is where I will store the activitiesList.

[
  {
    dutyDescription: SomeDuty,
    activityDescription: [
      {
        description: SomeActivity1
      },
      {
        description: SomeActivity2
      }
    ]
  }
]

What I want to do is start with an empty activitiesList and add the contents of the 'Activity Description' textfields (because there will be multiple textfields as well as other user input sources located inside of DynamicWidget in the production app) to the activitiesList but I can't figure out how to do it.

I'm still fairly new to Flutter, learning at a fast pace but I've been staring at this for way to long. Any help would be awesome.

Thanks in advance.

Ad

Answer

  1. Have you actually tried running this code? I can't imagine that a ListView inside an Expanded inside another ListView won't cause errors.

  2. Why did you make "duties" a list if "there should only be 1 element?"

  3. Solution you asked for:

Put the following function inside _MyAppState, next to addDynamic and submitData for the sake of tidyness

void update(String description) {
  setState(() {
    activitiesList.add({'description': description});
  });
}

then change DynamicWidget to something like

class DynamicWidget extends StatefulWidget {
  final Function update;

  DynamicWidget({required this.update});

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

class _DynamicWidgetState extends State<DynamicWidget> {
  String description = '';

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 80.0,
      width: 350.0,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SizedBox(
            width: 300.0,
            child: TextField(
              onChanged: (String text) {
                setState(() {
                  description = text;
                });
              },
              decoration: InputDecoration(labelText: 'Activity Description'),
            ),
          ),
          IconButton(
            icon: const Icon(Icons.arrow_forward),
            tooltip: 'Submit',
            onPressed: () => widget.update(description),
          ),
        ],
      ),
    );
  }
}

Call DynamicWidget like so

DynamicWidget(update: update)

If you print activitiesList before and after submitting you will see that whatever you type has been added.

Ad
source: stackoverflow.com
Ad