Home > Software design >  Can't pass data to next screen because selection state is unknown
Can't pass data to next screen because selection state is unknown

Time:05-20

I'm working on this app, that is supposed to tell you what kind of wine, or meal, you can enjoy with the specific meal or wine you're currently enjoying. Im currently at the point to make the user able to search for the wine/meal, he or she is currently enjoying to then ask his/her sommelier what wine/meal would fit best. Now my structure looks like this: The user opens the screen and he sees a ListView.builder of the wines/meals he can choose from that aren't ruled out rom the selection before (type...). Now I implemented a search that uses the fact of making a stateful list variable from the firebase data to then be able to search the list and display the results. Now to display these results I wrote a widget called PrefInformationCard which basically is a card that displays all the relevant data... Now to indicate to the user that the card he/she tapped on was selected I implemented a boolean check that changes the color of the widget. There are two problems with that. I implemented it in the widget which means on the screen and in the list view builder, I dont know, whether a card is selected and what card is selected so I can't pass the selected data to the next screen. Now the other problem is that there is the possibility of selecting multiple Cards at once.

In this first document you will see the code for the step 4 screen:

class AskSomellierStep4Screen extends StatefulWidget {
  const AskSomellierStep4Screen({
    Key? key,
    required this.stepDescription,
    required this.wineOrMeal,
    required this.mealVSWine,
    required this.selectedCuisine,
  }) : super(key: key);

  final String wineOrMeal;
  final String stepDescription;
  final bool mealVSWine;
  final String selectedCuisine;

  @override
  State<AskSomellierStep4Screen> createState() =>
      _AskSomellierStep4ScreenState();
}

class _AskSomellierStep4ScreenState extends State<AskSomellierStep4Screen> {
  List<String> dataLabel = [
    'Home',
    'Search',
    'Account',
  ];
  late Future resultsLoaded;
  List<IconData> data = [
    CustomIcons.home,
    CustomIcons.search,
    CustomIcons.user,
  ];

  getResults() async {
    var data = await FirebaseFirestore.instance
        .collection(widget.wineOrMeal)
        .where('type', isEqualTo: widget.selectedCuisine)
        .get();
    setState(() {
      allResults = data.docs;
    });
    filterResultsList();
    return data.docs;
  }

  List allResults = [];

  List filteredResults = [];

  onSearchChanged() {
    print(searchController.text);
    filterResultsList();
  }

  filterResultsList() {
    var showResults = [];

    if (searchController.text != '') {
      // we have a search parameter
      for (var searchSnapshot in allResults) {
        var name = searchSnapshot['name'].toLowerCase();

        if (name.contains(searchController.text.toLowerCase())) {
          showResults.add(searchSnapshot);
        }
      }
    } else {
      // we do not have a search parameter
      showResults = List.from(allResults);
    }
    setState(() {
      filteredResults = showResults;
    });
  }

  TextEditingController searchController = TextEditingController();

  @override
  void initState() {
    super.initState();
    searchController.addListener(onSearchChanged);
  }

  @override
  void dispose() {
    searchController.removeListener(onSearchChanged);
    searchController.dispose();
    super.dispose();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    resultsLoaded = getResults();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          HomeScreenHeader(
            subText: 'Wine and Food',
            mainText: 'Ask your Somellier',
            boxWidth: 238,
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 172,
              top: 92,
            ),
            child: Text(
              'Step 4',
              style: GoogleFonts.poppins(
                textStyle: TextStyle(
                  color: Theme.of(context).indicatorColor,
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 97,
            ),
            child: Text(
              widget.stepDescription,
              style: GoogleFonts.poppins(
                textStyle: TextStyle(
                  color: Theme.of(context).primaryColorLight,
                  fontSize: 14,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 35,
              top: 25,
            ),
            child: Stack(
              children: [
                Container(
                  height: 35,
                  width: 320,
                  decoration: BoxDecoration(
                    color: Theme.of(context).primaryColor,
                    borderRadius: BorderRadius.circular(20.0),
                  ),
                ),
                Positioned(
                  left: 10,
                  top: 14.5,
                  child: SizedBox(
                    height: 21,
                    width: 300,
                    child: TextField(
                      controller: searchController,
                      style: GoogleFonts.poppins(
                          textStyle: const TextStyle(
                              color: Colors.white,
                              fontSize: 14,
                              fontWeight: FontWeight.bold)),
                      decoration: InputDecoration(
                        hintText: 'Search',
                        hintStyle: GoogleFonts.poppins(
                          textStyle: TextStyle(
                              color: Theme.of(context).hintColor,
                              fontSize: 16,
                              fontWeight: FontWeight.bold),
                        ),
                        border: InputBorder.none,
                      ),
                    ),
                  ),
                ),
                Positioned(
                  left: 320 - 35,
                  top: 119 - 110,
                  child: SizedBox(
                    height: 17,
                    width: 17,
                    child: SvgPicture.asset(
                      'assets/icons/general/search.svg',
                      color: Theme.of(context).indicatorColor,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 35,
              top: 15,
            ),
            child: Text(
              'Popular Choices',
              style: GoogleFonts.poppins(
                textStyle: const TextStyle(
                  color: Colors.white,
                  fontSize: 14,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 35,
              top: 5,
            ),
            child: SizedBox(
              height: 370,
              width: 320,
              child: Center(
                child: ListView.builder(
                  dragStartBehavior: DragStartBehavior.down,
                  scrollDirection: Axis.vertical,
                  itemCount: filteredResults.length,
                  itemBuilder: (context, index) => widget.mealVSWine
                      ? PrefInformationCardWine(
                          snapShotDocument: filteredResults[index],
                          cardColor: Theme.of(context).primaryColor,
                          selected: false,
                        )
                      : PrefInformationCardMeal(
                          snapshotDocument: filteredResults[index],
                          cardColor: Theme.of(context).primaryColor,
                          selected: false),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 35,
              top: 25,
            ),
            child: SubmitSettingChangesButton(
              buttonText: 'Continue',
              cancelText: 'Cancel',
              cancelOnTap: () {
                Navigator.pop(context);
              },
              continueOnTap: () {
                Navigator.pushAndRemoveUntil(
                    context,
                    createRoute(FindingRecommendationScreen()),
                    (route) => false);
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              top: 15,
              left: 171,
            ),
            child: GestureDetector(
              onTap: () {
                Navigator.pop(context);
              },
              child: SizedBox(
                height: 16,
                width: 47,
                child: Center(
                  child: Text(
                    'Go Back',
                    style: GoogleFonts.poppins(
                      textStyle: TextStyle(
                        color: Theme.of(context).indicatorColor,
                        fontSize: 11,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

This is the relevant code for the PrefInformationCard:

class PrefInformationCardWine extends StatefulWidget {
  PrefInformationCardWine({
    Key? key,
    required this.snapShotDocument,
    required this.cardColor,
    required this.selected,
  }) : super(key: key);

  DocumentSnapshot snapShotDocument;
  Color cardColor;
  bool selected;

  @override
  State<PrefInformationCardWine> createState() =>
      _PrefInformationCardWineState();
}

class _PrefInformationCardWineState extends State<PrefInformationCardWine> {
  Color backgroundColor(context, selected) {
    setState(() {
      if (widget.selected == true) {
        widget.cardColor = Theme.of(context).primaryColorLight;
      } else {
        widget.cardColor = Theme.of(context).primaryColor;
      }
    });
    return widget.cardColor;
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        bottom: 25,
      ),
      child: GestureDetector(
        onTap: () {
          setState(() {
            widget.selected = !widget.selected;
          });
        },
        child: Stack(
          children: [
            Container(
              height: 185,
              width: 320,
              decoration: BoxDecoration(
                color: backgroundColor(context, widget.selected),
                boxShadow: [
                  BoxShadow(
                    offset: const Offset(0, 4),
                    color: const Color(0xff000000).withOpacity(.25),
                  ),
                ],
                borderRadius: BorderRadius.circular(
                  35,
                ),
              ),
            ),
...

I would be glad if someone could help me with this issue because I frankly dont have a clue, how to solve this. Thank you in advance:)


Padding(
            padding: const EdgeInsets.only(
              left: 35,
              top: 25,
            ),
            child: SubmitSettingChangesButton(
              buttonText: 'Continue',
              cancelText: 'Cancel',
              cancelOnTap: () {
                Navigator.pop(context);
              },
              continueOnTap: () {
                Navigator.pushAndRemoveUntil(
                    context,
                    createRoute(FindingRecommendationScreen(
                      snapshotName: _selectedSnapshot,
                    )),
                    (route) => false);
              },
            ),
          ),

That's how I wanted to pass the Data

CodePudding user response:

Rather than dealing with the selected state within your cards, you should use a callback to set the state in your step 4 screen.

(This will rebuild the entire step 4 screen, but I'm unsure of another way with just using setState)

For example:

  late DocumentSnapshot _selectedSnapshot;
  final List filteredResults = [];
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 370,
      width: 320,
      child: Center(
        child: ListView.builder(
          dragStartBehavior: DragStartBehavior.down,
          scrollDirection: Axis.vertical,
          itemCount: filteredResults.length,
          itemBuilder: (context, index) => widget.mealVSWine
              ? PrefInformationCardWine(
                  snapShotDocument: filteredResults[index],
                  cardColor: Theme.of(context).primaryColor,
                  isSelected: filteredResults[index] == _selectedSnapshot,
                  onSelected: () {
                    setState(() {
                      _selectedSnapshot = filteredResults[index];
                    });
                  },
                )
              : PrefInformationCardMeal(
                  snapshotDocument: filteredResults[index],
                  cardColor: Theme.of(context).primaryColor,
                  isSelected: filteredResults[index] == _selectedSnapshot,
                  onSelected: () {
                    setState(() {
                      _selectedSnapshot = filteredResults[index];
                    });
                  },
                ),
        ),
      ),
    );
  }
}

Then in your card:


class PrefInformationCardWine extends StatefulWidget {
  PrefInformationCardWine({
    Key? key,
    required this.snapShotDocument,
    required this.cardColor,
    required this.isSelected,
    required this.onSelected,
  }) : super(key: key);

  DocumentSnapshot snapShotDocument;
  Color cardColor;
  bool isSelected;
  VoidCallback onSelected;

  @override
  State<PrefInformationCardWine> createState() =>
      _PrefInformationCardWineState();
}

class _PrefInformationCardWineState extends State<PrefInformationCardWine> {
  Color backgroundColor(context, selected) {
    setState(() {
      if (widget.isSelected == true) {
        widget.cardColor = Theme.of(context).primaryColorLight;
      } else {
        widget.cardColor = Theme.of(context).primaryColor;
      }
    });
    return widget.cardColor;
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        bottom: 25,
      ),
      child: GestureDetector(
        onTap: widget.onSelected,
        child: Stack(
          children: [
            Container(
              height: 185,
              width: 320,
              decoration: BoxDecoration(
                color: backgroundColor(context, widget.isSelected),
                boxShadow: [
                  BoxShadow(
                    offset: const Offset(0, 4),
                    color: const Color(0xff000000).withOpacity(.25),
                  ),
                ],
                borderRadius: BorderRadius.circular(
                  35,
                ),
              ),
            )
...
  • Related