[Solved] The text in a button doesn't change according to RadioList that I choose | Flutter

Question

Asked by Adhitya R on January 10, 2022 (source).

I made a button using InkWell that if touched shows a showModalBottomSheet that contains a RadioListTile inside a ListView. By default, the button will show a text 'Pilih Pedagang' and it will changes according to the RadioListTile that I choose.

here's the code:

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

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int selectedValue = -1;
  List<String> merchants = ['Bang Ongot', 'Bang Kotan', 'Bang Bangtut', 'Bang BCA'];
  String merchantName = 'Pilih Pedagang';

  merchantList() {
    showModalBottomSheet(
        context: context,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20),
            topRight: Radius.circular(20),
          ),
        ),
        builder: (context) {
          return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
            return SizedBox(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              child: Column(children: [
                Padding(
                  padding: const EdgeInsets.only(top: 5, right: 10),
                  child: Align(
                    alignment: Alignment.topRight,
                    child: IconButton(
                      padding: EdgeInsets.zero,
                      onPressed: () => Navigator.pop(context),
                      icon: const Icon(
                        Icons.close,
                        color: Color(0xff31708F),
                      ),
                    ),
                  ),
                ),
                SingleChildScrollView(
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.height * 0.47,
                    child: ListView.builder(
                      itemCount: merchants.length,
                      itemBuilder: (context, index) {
                        return RadioListTile(
                          value: index,
                          groupValue: selectedValue,
                          title: Text(merchants.elementAt(index)),
                          secondary: OutlinedButton(
                            onPressed: () {},
                            child: const Text('View Profile'),
                          ),
                          onChanged: (value) {
                            setState(() =>
                            selectedValue = value as int);
                            merchantName = merchants.elementAt(index);
                            Navigator.pop(context);
                          },
                        );
                      }
                    ),
                  ),
                ),
              ]),
            );
          });
        });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider<ApplicationColor>(
        create: (BuildContext context) => ApplicationColor(),
        child: Scaffold(
          appBar: AppBar(
            title: const Text(
                "Test",
                style: TextStyle(color: Colors.white),
              ),
            backgroundColor: Colors.black,
            ),
          body: Container(
            padding: const EdgeInsets.only(top: 20,),
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            child: Align(
              alignment: Alignment.topCenter,
              child: InkWell(
                onTap: merchantList,
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      merchantName,
                      style: const TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.w500,
                          color: Color(0xff31708F)),
                    ),
                    const SizedBox(
                      width: 5,
                    ),
                    const Icon(
                      Icons.expand_more,
                      color: Color(0xff31708F),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

The problem is, the button's text is not updated until I CTRL+S the code in AndroidStudio. How can I solve this?

Answer

Question answered by Ramin (source).

When you use setState inside the modal it will only rebuild the affected widgets inside the modal, not the ones outside. This is because you're using a StatefulBuilder with its own setState which shadows the outer one. One way is to remove the StatefulBuilder or use another name for StateSetter so that it doesn't shadow the outer setState. Although this could look weird and it's better to either remove the StatefulBuilder or make the merchantList method async and await the call to showModalBottomSheet. Use setState after that to update the affected widgets outside modal.

Another way is to get back the selected name from your modal and then use setState to update your screen. The way you do that is when you call Navigator.pop, you provide the selected name after context like this: Navigator.pop(context, merchantName);. Then you use await when calling showModalBottomSheet and save the returned value in a variable. After that you just use setState to update the text if the user selected a merchant and the returned value isn't null. For your case the first way works just fine. But if you need to separate the code to showing modal from your widget this is how you could go about it. Here is the full code:

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int selectedValue = -1;
  List<String> merchants = ['Bang Ongot', 'Bang Kotan', 'Bang Bangtut', 'Bang BCA'];
  String merchantName = 'Pilih Pedagang';

  merchantList() async {
    final name = await showModalBottomSheet<String>(
        context: context,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20),
            topRight: Radius.circular(20),
          ),
        ),
        builder: (context) {
          return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
            return SizedBox(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              child: Column(children: [
                Padding(
                  padding: const EdgeInsets.only(top: 5, right: 10),
                  child: Align(
                    alignment: Alignment.topRight,
                    child: IconButton(
                      padding: EdgeInsets.zero,
                      onPressed: () => Navigator.pop(context),
                      icon: const Icon(
                        Icons.close,
                        color: Color(0xff31708F),
                      ),
                    ),
                  ),
                ),
                SingleChildScrollView(
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.height * 0.47,
                    child: ListView.builder(
                        itemCount: merchants.length,
                        itemBuilder: (context, index) {
                          return RadioListTile(
                            value: index,
                            groupValue: selectedValue,
                            title: Text(merchants.elementAt(index)),
                            secondary: OutlinedButton(
                              onPressed: () {},
                              child: const Text('View Profile'),
                            ),
                            onChanged: (value) {
                              setState(() => selectedValue = value as int);
                              merchantName = merchants.elementAt(index);
                              Navigator.pop(context, merchantName);
                            },
                          );
                        }),
                  ),
                ),
              ]),
            );
          });
        });

    if (name != null) {
      setState(() {
        merchantName = name;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            "Test",
            style: TextStyle(color: Colors.white),
          ),
          backgroundColor: Colors.black,
        ),
        body: Container(
          padding: const EdgeInsets.only(
            top: 20,
          ),
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child: Align(
            alignment: Alignment.topCenter,
            child: InkWell(
              onTap: merchantList,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    merchantName,
                    style: const TextStyle(
                        fontSize: 20, fontWeight: FontWeight.w500, color: Color(0xff31708F)),
                  ),
                  const SizedBox(
                    width: 5,
                  ),
                  const Icon(
                    Icons.expand_more,
                    color: Color(0xff31708F),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int selectedValue = -1;
  List<String> merchants = ['Bang Ongot', 'Bang Kotan', 'Bang Bangtut', 'Bang BCA'];
  String merchantName = 'Pilih Pedagang';

  merchantList() async {
    final name = await showModalBottomSheet<String>(
        context: context,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20),
            topRight: Radius.circular(20),
          ),
        ),
        builder: (context) {
          return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
            return SizedBox(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              child: Column(children: [
                Padding(
                  padding: const EdgeInsets.only(top: 5, right: 10),
                  child: Align(
                    alignment: Alignment.topRight,
                    child: IconButton(
                      padding: EdgeInsets.zero,
                      onPressed: () => Navigator.pop(context),
                      icon: const Icon(
                        Icons.close,
                        color: Color(0xff31708F),
                      ),
                    ),
                  ),
                ),
                SingleChildScrollView(
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.height * 0.47,
                    child: ListView.builder(
                        itemCount: merchants.length,
                        itemBuilder: (context, index) {
                          return RadioListTile(
                            value: index,
                            groupValue: selectedValue,
                            title: Text(merchants.elementAt(index)),
                            secondary: OutlinedButton(
                              onPressed: () {},
                              child: const Text('View Profile'),
                            ),
                            onChanged: (value) {
                              setState(() => selectedValue = value as int);
                              merchantName = merchants.elementAt(index);
                              Navigator.pop(context, merchantName);
                            },
                          );
                        }),
                  ),
                ),
              ]),
            );
          });
        });

    if (name != null) {
      setState(() {
        merchantName = name;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            "Test",
            style: TextStyle(color: Colors.white),
          ),
          backgroundColor: Colors.black,
        ),
        body: Container(
          padding: const EdgeInsets.only(
            top: 20,
          ),
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child: Align(
            alignment: Alignment.topCenter,
            child: InkWell(
              onTap: merchantList,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    merchantName,
                    style: const TextStyle(
                        fontSize: 20, fontWeight: FontWeight.w500, color: Color(0xff31708F)),
                  ),
                  const SizedBox(
                    width: 5,
                  ),
                  const Icon(
                    Icons.expand_more,
                    color: Color(0xff31708F),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Video Answers on YouTube

ANDROID-STUDIO DART FLUTTER
SHARE: