Dropdownbutton Statemanagement - Flutter

Viewed 49

I want to create an evaluation app in flutter with multiple dropdownbuttons. The dropdownbuttons should contain a text with a value, for example: dropdownbutton1: "Text"; Value(2), dropdownbutton2: "Text"; Value2(4) and there is also an another button "evaluate", if i click on the "evaluate" button it should go to the next screen and the next screen displays the total of the value (it should be 6= value(2) + value2(4).

My next thought would be statemanagement, but i dont know how to do it right now. I searched everywhere in the internet but couldnt find anything.

I am new to flutter. Is there a way to do it with statemanagement and how it could be look like?

1 Answers

Definitely you should implement a flavor of state management, both for the communication between the pages as well as to feed data to the dropdown menus, and holding on to the selected values from the dropdowns.

My suggestion would be to go with something as simple as the Provider state management strategy, and leverage the widgets that facilitate listening to changes occurring on the application. This strategy makes your app more decouples, maintains a clean separation of concerns and allows for good maintenance.

You can create a Provided service (i.e. DropdownService) that holds to the values of the dropdown menus as a list of dropdown options, as well as two properties that hold on to the selected values from the dropdowns (i.e. selectedFirstOption, selectedSecondOption). Upon triggering changes on both DropDownButton widgets, you can trigger a notification for the widget to rebuild itself, updating its selections, and holding on to the selected value.

A button will trigger the evaluation and navigation to the next page, only if both selectedFirstOption and selectedSecondOption are not null, then the next page also consumes the provided DropdownService, pulls the data persisted for both selections, add the values and displays it to the user.

I threw something together as an example to illustrate my point below:


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

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => DropdownService()
        )
      ],
      child: MyApp()
    )
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    
    return Consumer<DropdownService>(
      builder: (context, dropdownService, child) {
        return Container(
            padding: const EdgeInsets.all(30),
            child: Column(
              children: [
                const Text('First dropdown:'),
                DropdownButton<DropdownOption>(
                  value: dropdownService.selectedFirstOption,
                  icon: const Icon(Icons.arrow_drop_down),
                  elevation: 16,
                  onChanged: (DropdownOption? newValue) {
                      dropdownService.selectedFirstOption = newValue!;
                  },
                  items: dropdownService.firstDropdown.map((DropdownOption option) {
                    return DropdownMenuItem<DropdownOption>(
                      value: option,
                      child: Text(option.text!),
                    );
                  }).toList(),
                ),
                const SizedBox(height: 20),
                const Text('Second dropdown:'),
                DropdownButton<DropdownOption>(
                  value: dropdownService.selectedSecondOption,
                  icon: const Icon(Icons.arrow_drop_down),
                  elevation: 16,
                  onChanged: (DropdownOption? newValue) {
                      dropdownService.selectedSecondOption = newValue!;
                  },
                  items: dropdownService.secondDropdown.map((DropdownOption option) {
                    return DropdownMenuItem<DropdownOption>(
                      value: option,
                      child: Text(option.text!),
                    );
                  }).toList(),
                ),
                const SizedBox(height: 20),
                Material(
                  color: Colors.amber,
                  child: TextButton(
                    onPressed: dropdownService.selectedFirstOption != null 
                      && dropdownService.selectedSecondOption != null ? () {
                      
                      Navigator.of(context).push(
                        MaterialPageRoute(builder: (context) => NextPage())
                      );
                    } : null,
                    child: const Text('Evaluate', style: TextStyle(color: Colors.black)
                    )
                  )
                )
              ]
            )
          );
      }
    );
  }
}

class NextPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    DropdownService dropdownService = Provider.of<DropdownService>(context, listen: false);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('The result of ${dropdownService.selectedFirstOption!.text!} and ${dropdownService.selectedSecondOption!.text!}:'),
            const SizedBox(height: 20),
            Text('${dropdownService.selectedFirstOption!.value! + dropdownService.selectedSecondOption!.value!}',
              style: const TextStyle(fontSize: 50)
            ),
          ],
        ),
      ),
    );
  }
}

class DropdownService extends ChangeNotifier {
  
  List<DropdownOption> firstDropdown = [
    DropdownOption(text: 'Value (5)', value: 5),
    DropdownOption(text: 'Value (6)', value: 6),
    DropdownOption(text: 'Value (7)', value: 7),
  ];
  
  DropdownOption? _selectedFirstOption; // = DropdownOption(text: 'Select Value', value: -1);
  DropdownOption? _selectedSecondOption; // = DropdownOption(text: 'Select Value', value: -1);

  DropdownOption? get selectedFirstOption => _selectedFirstOption;
  DropdownOption? get selectedSecondOption => _selectedSecondOption;

  set selectedFirstOption(DropdownOption? value) {
    _selectedFirstOption = value;
    notifyListeners();
  }

  set selectedSecondOption(DropdownOption? value) {
    _selectedSecondOption = value;
    notifyListeners();
  } 
  
  List<DropdownOption> secondDropdown = [
    DropdownOption(text: 'Value (1)', value: 1),
    DropdownOption(text: 'Value (2)', value: 2),
    DropdownOption(text: 'Value (3)', value: 3),
  ];
}


class DropdownOption {
  String? text;
  double? value;
  
  DropdownOption({ this.text, this.value });
}

If you run this code (also provided as a Gist) through DartPad.dev, you should see the following output:

enter image description here

Related