skip to Main Content

enter image description here

If user clicks on any of the checkbox then a textformfield should be displayed below the checkbox to enter the unit configuration.

For example when user clicks on a checkbox 1BHK then a textformfield should be visible under it and if he clicks on 2 BHK then a textformfield should be visible under it and so on.

Here is my code:

import 'package:flutter/material.dart';

class CheckBoxes extends StatefulWidget {
  const CheckBoxes({Key? key}) : super(key: key);

  @override
  State<CheckBoxes> createState() => _CheckBoxesState();
}

class _CheckBoxesState extends State<CheckBoxes> {
  static List<String> selectedOptions = [];
  List<CheckboxListTile>? bhkOptions;

  @override
  void initState() {
    super.initState();
    selectedOptions = [];
  }

  @override
  Widget build(BuildContext context) {
    bhkOptions = [
      CheckboxListTile(
        title: Text('1 BHK'),
        value: selectedOptions.contains('1 BHK'),
        onChanged: (bool? value) {
          setState(() {
            if (value!) {
              selectedOptions.add('1 BHK');
            } else {
              selectedOptions.remove('1 BHK');
            }
          });
        },
      ),
      CheckboxListTile(
        title: Text('2 BHK'),
        value: selectedOptions.contains('2 BHK'),
        onChanged: (bool? value) {
          setState(() {
            if (value!) {
              selectedOptions.add('2 BHK');
            } else {
              selectedOptions.remove('2 BHK');
            }
          });
        },
      ),
      CheckboxListTile(
        title: Text('3 BHK'),
        value: selectedOptions.contains('3 BHK'),
        onChanged: (bool? value) {
          setState(() {
            if (value!) {
              selectedOptions.add('3 BHK');
            } else {
              selectedOptions.remove('3 BHK');
            }
          });
        },
      ),
      CheckboxListTile(
        title: Text('4 BHK'),
        value: selectedOptions.contains('4 BHK'),
        onChanged: (bool? value) {
          setState(() {
            if (value!) {
              selectedOptions.add('4 BHK');
            } else {
              selectedOptions.remove('4 BHK');
            }
          });
        },
      ),
      CheckboxListTile(
        title: Text('5+ BHK'),
        value: selectedOptions.contains('5+ BHK'),
        onChanged: (bool? value) {
          setState(() {
            if (value!) {
              selectedOptions.add('5+ BHK');
            } else {
              selectedOptions.remove('5+ BHK');
            }
          });
        },
      ),
    ];

    return Column(
      children: bhkOptions!,
    );
  }
}

2

Answers


  1. I think you should use a custom class that implement by yourself instead of CheckboxListTile

    class CustomCheckboxItem extends StatefulWidget {
    
      final String title;
    
      const CustomCheckboxItem({super.key, required this.title});
    
      @override
      State<StatefulWidget> createState() => _CustomCheckboxItemState();
    
    }
    
    class _CustomCheckboxItemState extends State<CustomCheckboxItem> {
    
      bool _isDisplayTextField = false;
    
      Color getColor(Set<MaterialState> states) {
        const Set<MaterialState> interactiveStates = <MaterialState>{
          MaterialState.pressed,
          MaterialState.hovered,
          MaterialState.focused,
        };
        if (states.any(interactiveStates.contains)) {
          return Colors.blue;
        }
        return Colors.red;
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Row(
              children: [
                Text(widget.title),
                const Spacer(),
                Checkbox(
                  checkColor: Colors.white,
                  fillColor: MaterialStateProperty.resolveWith(getColor),
                  value: _isDisplayTextField,
                  onChanged: (bool? value) {
                    setState(() {
                      _isDisplayTextField = value ?? false;
                    });
                  },
                ),
              ],
            ),
            if (_isDisplayTextField)
              const TextField()
          ],
        );
      }
    
    }
    Login or Signup to reply.
  2. As suggested in https://api.flutter.dev/flutter/material/CheckboxListTile-class.html (below the headline "CheckboxListTile isn’t exactly what I want") you can create a custom checkbox widget. The class CheckBoxTile doesn’t contain properties to set a child widget below the checkbox.

    The following code snippets implements a custom checkbox class MyCustomCheckbox. The TextFormField is the second item in a column and is only shown if the value property of MyCustomCheckbox isn’t null. Which exactly means that the checkbox is selected. The first item of the column includes the checkbox label and the checkbox itself.

    class MyCustomCheckbox extends StatelessWidget {
      const MyCustomCheckbox({
        super.key,
        required this.text,
        required this.value,
        required this.onChanged,
        this.padding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16)
      });
    
      final Text text;
      final EdgeInsets padding;
      final bool value;
      final ValueChanged<bool> onChanged;
    
      @override
      Widget build(BuildContext context) {
        return InkWell(
          onTap: () {
            onChanged(!value);
          },
          child: Padding(
            padding: padding,
            child: Column(
              children: [
                Row(
                  children: <Widget>[
                    Expanded(child: text),
                    Checkbox(
                      value: value,
                      onChanged: (bool? newValue) {
                        onChanged(newValue!);
                      },
                    ),
                  ],
                ),
                value
                    //.........................................
                    //HERE IS THE TextFormField WIDGET
                    //.........................................
                    ? TextFormField(
                        decoration: const InputDecoration(
                          icon: Icon(Icons.arrow_right),
                          labelText: 'You've selected this option, please specify...',
                        )
                      )
                    //SizedBox is just an empty placeholder
                    : const SizedBox() 
              ],
            ),
          ),
        );
      }
    }
    
    
    class CheckBoxes extends StatefulWidget {
      const CheckBoxes({Key? key}) : super(key: key);
    
      @override
      State<CheckBoxes> createState() => _CheckBoxesState();
    }
    
    class _CheckBoxesState extends State<CheckBoxes> {
      static List<String> selectedOptions = [];
      List<MyCustomCheckbox>? bhkOptions;
    
      @override
      void initState() {
        super.initState();
        selectedOptions = [];
      }
    
      @override
      Widget build(BuildContext context) {
        bhkOptions = [
          MyCustomCheckbox(
            text: const Text('1 BHK'),
            value: selectedOptions.contains('1 BHK'),
            onChanged: (bool? value) {
              setState(() {
                if (value!) {
                  selectedOptions.add('1 BHK');
                } else {
                  selectedOptions.remove('1 BHK');
                }
              });
            },
          ),
          MyCustomCheckbox(
            text: const Text('2 BHK'),
            value: selectedOptions.contains('2 BHK'),
            onChanged: (bool? value) {
              setState(() {
                if (value!) {
                  selectedOptions.add('2 BHK');
                } else {
                  selectedOptions.remove('2 BHK');
                }
              });
            },
          ),
          MyCustomCheckbox(
            text: const Text('3 BHK'),
            value: selectedOptions.contains('3 BHK'),
            onChanged: (bool? value) {
              setState(() {
                if (value!) {
                  selectedOptions.add('3 BHK');
                } else {
                  selectedOptions.remove('3 BHK');
                }
              });
            },
          ),
          MyCustomCheckbox(
            text: const Text('4 BHK'),
            value: selectedOptions.contains('4 BHK'),
            onChanged: (bool? value) {
              setState(() {
                if (value!) {
                  selectedOptions.add('4 BHK');
                } else {
                  selectedOptions.remove('4 BHK');
                }
              });
            },
          ),
          MyCustomCheckbox(
            text: const Text('5+ BHK'),
            value: selectedOptions.contains('5+ BHK'),
            onChanged: (bool? value) {
              setState(() {
                if (value!) {
                  selectedOptions.add('5+ BHK');
                } else {
                  selectedOptions.remove('5+ BHK');
                }
              });
            },
          ),
        ];
    
        return Column(
          children: bhkOptions!,
        );
      }
    }
    

    enter image description here


    EDIT

    You’ve asked in your comment how to access the value set by the checkboxes in another page.

    Maybe this guide from the official flutter docs https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple
    is worthwhile reading for you. At least it was for me quite helpful.

    Your widgets are organized in a widget tree. In order to edit one variable in widget A and access this variable in a different widget B you should declare this variable in a widget that is a common ancestor of A and B.

    Then the question arises: How to pass the info/the set handler down the widget tree to A and B. One solution passing it down using constructor parameters, another is using InheritedWidgets.

    In the following approach I’ve used the provider package, which basically simplifies use of the InheritedWidgets.
    Here, the common ancestor is the MyApp widget which manages its (state) variable in MyAppState.

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    void main() => runApp(const MyApp());
    
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      static const String _title = 'Flutter Code Sample';
    
      @override
      Widget build(BuildContext context) {
        return ChangeNotifierProvider(
          create: (context) => MyAppState(),
          child: const MaterialApp(
            title: _title,
            home: StartPage(),
          ),
        );
      }
    }
    
    
    class MyAppState extends ChangeNotifier {
    
      //.................................
      //
      // here are the variables that must be available to more than one page and which are gonna
      // change while the app's running
      //
      //...........................................................................  
      List<String> bhkOptions = [];
      String city = 'Rome';
    
    
      bhkOptionsAdd(String s){
        bhkOptions.add(s);
        notifyListeners();
      }
    
      bhkOptionsRemove(String s){
        bhkOptions.remove(s);
        notifyListeners();
      }
    
    }
    
    
    //from this simple start page you have access to other different pages
    class StartPage extends StatelessWidget{
      const StartPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text("AppBar")),
          body: Row(
            children: [
              ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(
                      builder: (context) => const SelectOptionsPage(),
                    ));
                  },
                  child: Text("Options")
              ),
              ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(
                      builder: (context) => const ClientPreferencesPage(),
                    ));
                  },
                  child: Text("Client Preferences"))
            ],
          ),
        );
      }
    
    }
    
    
    //on this page you gonna set the checkboxes
    class SelectOptionsPage extends StatelessWidget {
      const SelectOptionsPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Select Options')),
          body: const CheckBoxes()
        );
      }
    }
    
    //this pages uses the values which have been set in the screen with the checkboxes
    class ClientPreferencesPage extends StatelessWidget {
      const ClientPreferencesPage();
    
      @override
      Widget build(BuildContext context) {
        final myAppState = Provider.of<MyAppState>(context);
        String prefers = myAppState.bhkOptions.join(", ");
    
        return Scaffold(
            appBar: AppBar(title: const Text("Client page")),
            body: Center(
                child: Text("Client prefers $prefers real estate in ${myAppState.city}")));
      }
    }
    
    
    //this widget is used in SelectOptionsPage
    class CheckBoxes extends StatelessWidget {
      const CheckBoxes({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final appState = Provider.of<MyAppState>(context);
    
    
        List<Widget> bhkOptions = <Widget>[
          MyCustomCheckbox(
            text: const Text('1 BHK'),
            value: appState.bhkOptions.contains('1 BHK'),
            onChanged: (bool? value) {
                if (value!) {
                  //myAppState.selectedBhkOptions
                  appState.bhkOptionsAdd('1 BHK');
                } else {
                  appState.bhkOptionsRemove('1 BHK');
                }
            },
          ),
          MyCustomCheckbox(
            text: const Text('2 BHK'),
            value: appState.bhkOptions.contains('2 BHK'),
            onChanged: (bool? value) {
                if (value!) {
                  appState.bhkOptionsAdd('2 BHK');
                } else {
                  appState.bhkOptionsRemove('2 BHK');
                }
            },
          ),
          MyCustomCheckbox(
            text: const Text('3 BHK'),
            value: appState.bhkOptions.contains('3 BHK'),
            onChanged: (bool? value) {
                if (value!) {
                  appState.bhkOptionsAdd('3 BHK');
                } else {
                  appState.bhkOptionsRemove('3 BHK');
                }
            },
          ),
          MyCustomCheckbox(
            text: const Text('4 BHK'),
            value: appState.bhkOptions.contains('4 BHK'),
            onChanged: (bool? value) {
                if (value!) {
                  appState.bhkOptionsAdd('4 BHK');
                } else {
                  appState.bhkOptionsRemove('4 BHK');
                }
            },
          ),
          MyCustomCheckbox(
            text: const Text('5+ BHK'),
            value: appState.bhkOptions.contains('5+ BHK'),
            onChanged: (bool? value) {
                if (value!) {
                  appState.bhkOptionsAdd('5+ BHK');
                } else {
                  appState.bhkOptionsRemove('5+ BHK');
                }
            },
          ),
        ];
    
        return Column(
          children: bhkOptions,
        );
      }
    }
    
    
    //this widget is used in CheckBoxes
    class MyCustomCheckbox extends StatelessWidget {
      const MyCustomCheckbox({
        super.key,
        required this.text,
        required this.value,
        required this.onChanged,
        this.padding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16)
      });
    
      final Text text;
      final EdgeInsets padding;
      final bool value;
      final ValueChanged<bool> onChanged;
    
      @override
      Widget build(BuildContext context) {
        return InkWell(
          onTap: () {
            onChanged(!value);
          },
          child: Padding(
            padding: padding,
            child: Column(
              children: [
                Row(
                  children: <Widget>[
                    Expanded(child: text),
                    Checkbox(
                      value: value,
                      onChanged: (bool? newValue) {
                        onChanged(newValue!);
                      },
                    ),
                  ],
                ),
                value
                    //.........................................
                    //HERE IS THE TextFormField WIDGET
                    //.........................................
                    ? TextFormField(
                        decoration: const InputDecoration(
                          icon: Icon(Icons.arrow_right),
                          labelText: 'You've selected this option, please specify...',
                        )
                      )
                    //SizedBox is just an empty placeholder
                    : const SizedBox() 
              ],
            ),
          ),
        );
      }
    }
    
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search