0

I have a bottom navigation bar and realized that the different pages/widgets that the navigator was going to were pretty much the exact same page (except for 2 parameters that changed). So instead of creating 2 pages/widgets which were pretty much identical (with only 2 differing parameters), I wanted to consolidate it into only one widget and pass the parameters from the page with the bottom navigator. The problem is that now that I did that it won't change the page it displays, or at least it won't change consistently (it usually will only show the page that corresponds to the first tab in the navigator (i.e., index = 0)). Here is my page with the bottom navigator:

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

  @override
  State<StatefulWidget> createState() {
    return _FreestylePageState();
  }
}

class _FreestylePageState extends State<FreestylePage> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: showCategory(_currentIndex),
      )),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: const [
          BottomNavigationBarItem(
              icon: Icon(Icons.looks_one_outlined),
              label: 'Single rope',
              backgroundColor: Color.fromRGBO(204, 16, 138, 1)),
          BottomNavigationBarItem(
              icon: Icon(Icons.looks_two_outlined),
              label: 'Double dutch',
              backgroundColor: Color.fromRGBO(204, 16, 138, 1)),
        ],
        onTap: (index) {
          if (mounted) {
            setState(() {
              _currentIndex = index;
            });
          }
        },
      ),
    );
  }

  showCategory(index) {
    if (index == 0) {
      return [
        WorkoutListPage(categoryIndex: 2, subCategories: Utils.srfDropdown)
      ];
    } else {
      return [
        WorkoutListPage(categoryIndex: 3, subCategories: Utils.ddfDropdown)
      ];
    }
  }
}

And the WorkoutListPage looks as follows:

class WorkoutListPage extends StatefulWidget {
  final int categoryIndex;
  final List<String> subCategories;
  const WorkoutListPage(
      {Key? key, required this.categoryIndex, required this.subCategories})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _WorkoutListPageState();
  }
}

class _WorkoutListPageState extends State<WorkoutListPage> {
  bool isLoading = true;
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) =>
      FutureBuilder<List<Map<String, dynamic>>>(
        future: MyCard.getData(widget.categoryIndex, widget.subCategories)!
            .whenComplete(() => setState(() {
                  isLoading = false;
                })),
        builder: ((context, snapshot) {
          if (snapshot.hasData && snapshot.data!.isNotEmpty) {
            return FutureBuilder<List<MyCard>>(
                future: MyCard.readData(snapshot.data),
                builder: (context, cards) {
                  if (cards.hasData) {
                    final card = cards.data!;
                    return Expanded(
                      child: ListView.builder(
                        padding: const EdgeInsets.all(16),
                        itemCount: card.length,
                        itemBuilder: (context, index) {
                          return MyCard.buildCard(card[index], context);
                        },
                      ),
                    );
                  } else {
                    return const Text("No data");
                  }
                });
          } else {
            return isLoading
                ? Column(
                    children: const [CircularProgressIndicator()],
                  )
                : const Text("You do not have any workouts yet");
          }
        }),
      );
}

This doesn't work, but ironically if I change my showCategory function in the widget with the bottom navigation bar to the following:

showCategory(index) {
    if (index == 0) {
      return [
        WorkoutListPage(categoryIndex: 2, subCategories: Utils.srfDropdown)
      ];
    } else {
      return [const FreestyleDDPage()];
    }
  }

where the FreestyleDDPage is the following:

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

  @override
  State<StatefulWidget> createState() {
    return _FreestyleDDPageState();
  }
}

class _FreestyleDDPageState extends State<FreestyleDDPage> {
  var isLoading = true;

  @override
  Widget build(BuildContext context) =>
      FutureBuilder<List<Map<String, dynamic>>>(
        future: MyCard.getData(3, Utils.ddfDropdown)!
            .whenComplete(() => setState(() {
                  isLoading = false;
                })),
        builder: ((context, snapshot) {
          if (snapshot.hasData && snapshot.data!.isNotEmpty) {
            return FutureBuilder<List<MyCard>>(
                future: MyCard.readData(snapshot.data),
                builder: (context, cards) {
                  if (cards.hasData) {
                    final card = cards.data!;
                    return Expanded(
                      child: ListView.builder(
                        padding: const EdgeInsets.all(16),
                        itemCount: card.length,
                        itemBuilder: (context, index) {
                          return MyCard.buildCard(card[index], context);
                        },
                      ),
                    );
                  } else {
                    return const Text("No data");
                  }
                });
          } else {
            return isLoading
                ? Column(
                    children: const [CircularProgressIndicator()],
                  )
                : const Text("You do not have any workouts yet");
          }
        }),
      );
}

then it works.

1 Answers1

0
import 'package:flutter/material.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 const MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
  static const List<Widget> _widgetOptions = <Widget>[
    CustomWidgetWithParametr(index: 0 , categoryName: "HOME"),
    CustomWidgetWithParametr(index: 1 , categoryName: "BUSINES"),
    CustomWidgetWithParametr(index: 2 , categoryName: "SCHOOL"),
    CustomWidgetWithParametr(index: 3 , categoryName: "Settings"),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BottomNavigationBar Sample'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
            backgroundColor: Colors.red,
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
            backgroundColor: Colors.green,
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
            backgroundColor: Colors.purple,
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'Settings',
            backgroundColor: Colors.pink,
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}


class CustomWidgetWithParametr extends StatefulWidget {
  const CustomWidgetWithParametr({Key? key, required this.index, required this.categoryName}) : super(key: key);
 final int index;
 final String categoryName;

  @override
  State<CustomWidgetWithParametr> createState() => _CustomWidgetWithParametrState();
}

class _CustomWidgetWithParametrState extends State<CustomWidgetWithParametr> {
  @override
  Widget build(BuildContext context) {

       return
         Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
           children: [
             Text(widget.index.toString()),
             Text(widget.categoryName),
           ],

       );


  }
}
  • I tried to match your code and it was better but still very slow (it also didn't always work from switching from tab 0 to 1 and back to 0). Idk if it has to do with my FutureBuilder slowing things down, cause if I change the WorkoutListPage to the following: `class _WorkoutListPageState extends State { bool isLoading = true; @override void initState() { super.initState(); } @override Widget build(BuildContext context) => Text(widget.categoryIndex.toString()); }` it changes screens without any lag. – FlutterBuilder Jan 13 '23 at 16:58
  • But it's probably not my FutureBuilder cause when I make one of the widgets in the _widgetOptions a different widget it loads immediately. – FlutterBuilder Jan 13 '23 at 17:14
  • you use async in your code, you have problems with `.whenComplete` your code is still running in the background, data has not been received, and the widget is already ready and displayed – Александр Инженер Jan 13 '23 at 19:54
  • I fixed the problem by adding a unique key to each widget option as so: `static final List _widgetOptions = [WorkoutListPage(key: UniqueKey(), categoryIndex: 0, subCategories: Utils.srsDropdown), WorkoutListPage( key: UniqueKey(), categoryIndex: 1, subCategories: Utils.ddsDropdown)];` – FlutterBuilder Jan 14 '23 at 01:58