md-siam
md-siam

Reputation: 225

How can I get data from three separate FutureBuilders widgets?

I am trying to display one SingleChildListView containing THREE seperate FutureBuilder with ListView.sperator. I am using Provider for fetching data from the SQFLite database.

Thi is my code:

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

  @override
  State<SelectCategoryPage> createState() => _SelectCategoryPageState();
}

class _SelectCategoryPageState extends State<SelectCategoryPage> {
  //
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: kBackgroundColor,
      appBar: _appBar(),
      body: _body(context),
      bottomNavigationBar: _bottomNavigationBar(),
    );
  }

  AppBar _appBar() {
    return AppBar(
      elevation: 0,
      backgroundColor: Colors.white,
      title: Text(
        'Select Category',
        style: appBarHeaderTStyle,
      ),
      iconTheme: const IconThemeData(color: Colors.black),
      actions: [
        Consumer<SelectCategoryViewModel>(
          builder: (context, provider, child) {
            return TextButton(
              child: Text(
                provider.getEditOptionData ? 'Save' : 'Edit',
                style: textButtonTStyle,
              ),
              onPressed: () {
                provider.toggleEditButton();
              },
            );
          },
        ),
      ],
    );
  }

  Widget _body(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
      child: Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              controller: null,
              physics: const BouncingScrollPhysics(),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const SizedBox(height: 12),
                  Text(
                    'Expense',
                    style: selectCategoryHeaderTStyle,
                  ),
                  const SizedBox(height: 16),
                  //
                  _expenseCategory(context),
                  //
                  const SizedBox(height: 24),
                  Text(
                    'Income',
                    style: selectCategoryHeaderTStyle,
                  ),
                  const SizedBox(height: 16),
                  //
                  _incomeCategory(context),
                  //
                  const SizedBox(height: 24),
                  Text(
                    'Other',
                    style: selectCategoryHeaderTStyle,
                  ),
                  const SizedBox(height: 16),
                  //
                  _otherCategory(context),
                  //
                  const SizedBox(height: 20),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  FutureBuilder<void> _expenseCategory(BuildContext context) {
    return FutureBuilder(
      future: Provider.of<SelectCategoryViewModel>(
        context,
        listen: false,
      ).selectCategoryByExpenseType(),
      builder: (context, snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
            return const Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            return Consumer<SelectCategoryViewModel>(
              child: const Center(child: Text('No Data')),
              builder: (context, expenseProvider, child) => expenseProvider
                      .data.isEmpty
                  ? child!
                  : ListView.separated(
                      shrinkWrap: true,
                      itemCount: expenseProvider.data.length,
                      physics: const BouncingScrollPhysics(),
                      separatorBuilder: (BuildContext context, int index) {
                        return const SizedBox(height: 16);
                      },
                      itemBuilder: (BuildContext context, int index) {
                        return SelectCategoryCard(
                          id: expenseProvider.data[index].id,
                          coloredIcon: expenseProvider.data[index].categoryIcon,
                          title: expenseProvider.data[index].categoryName,
                          isOptionDotsVisitable:
                              expenseProvider.getEditOptionData,
                        );
                      },
                    ),
            );
          default:
            return Container();
        }
      },
    );
  }

  FutureBuilder<void> _incomeCategory(BuildContext context) {
    return FutureBuilder(
      future: Provider.of<SelectCategoryViewModel>(
        context,
        listen: false,
      ).selectCategoryByIncomeType(),
      builder: (context, snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
            return const Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            return Consumer<SelectCategoryViewModel>(
              child: const Center(child: Text('No Data')),
              builder: (context, incomeProvider, child) => incomeProvider
                      .data.isEmpty
                  ? child!
                  : ListView.separated(
                      shrinkWrap: true,
                      itemCount: incomeProvider.data.length,
                      physics: const BouncingScrollPhysics(),
                      separatorBuilder: (BuildContext context, int index) {
                        return const SizedBox(height: 16);
                      },
                      itemBuilder: (BuildContext context, int index) {
                        return SelectCategoryCard(
                          id: incomeProvider.data[index].id,
                          coloredIcon: incomeProvider.data[index].categoryIcon,
                          title: incomeProvider.data[index].categoryName,
                          isOptionDotsVisitable:
                              incomeProvider.getEditOptionData,
                        );
                      },
                    ),
            );
          default:
            return Container();
        }
      },
    );
  }

  FutureBuilder<void> _otherCategory(BuildContext context) {
    return FutureBuilder(
      future: Provider.of<SelectCategoryViewModel>(
        context,
        listen: false,
      ).selectCategoryByOtherType(),
      builder: (context, snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
            return const Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            return Consumer<SelectCategoryViewModel>(
              child: const Center(child: Text('No Data')),
              builder: (context, otherProvider, child) => otherProvider
                      .data.isEmpty
                  ? child!
                  : ListView.separated(
                      shrinkWrap: true,
                      itemCount: otherProvider.data.length,
                      physics: const BouncingScrollPhysics(),
                      separatorBuilder: (BuildContext context, int index) {
                        return const SizedBox(height: 16);
                      },
                      itemBuilder: (BuildContext context, int index) {
                        return SelectCategoryCard(
                          id: otherProvider.data[index].id,
                          coloredIcon: otherProvider.data[index].categoryIcon,
                          title: otherProvider.data[index].categoryName,
                          isOptionDotsVisitable:
                              otherProvider.getEditOptionData,
                        );
                      },
                    ),
            );
          default:
            return Container();
        }
      },
    );
  }

  Widget _bottomNavigationBar() {
    return CustomBottomAppBar(
      buttonText: '+ Add New Category',
      onTapEvent: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const CreateNewCategoryPage(),
          ),
        );
      },
    );
  }
}

Only the last FutureBuilder is working properly, and other builders are showing the exact output as the last (3rd) FutureBuilder.

enter image description here

Upvotes: 0

Views: 35

Answers (1)

Piotr
Piotr

Reputation: 667

Went through the same thing once. You need to have only one future builder and a Future that gets all the data and assigns it. Something like this:

    class _DeviceInformationScreenState extends State<DeviceInformationScreen> {
  late StaticConfiguration staticConfiguration;
  late PackageInfo packageInfo;
  late DeviceData deviceData;
  late QuarkusHealthClient quarkusHealth;
  LisaInfo lisaInfo = LisaInfo();

  //TODO: add translations?

  Future<void> _getAppInfo() async {
    packageInfo = await PackageInfo.fromPlatform();
    staticConfiguration = await config.readStaticConfiguration();
    deviceData = await DeviceData.fromDevice();
    quarkusHealth = await QuarkusHealthClient.checkHealth(staticConfiguration.grpcServerHost);
    lisaInfo = await lisaInfo.getLisaInfo();
  }

  @override
  Widget build(BuildContext context) {
    return buildToolsScaffold(context,
        body: FutureBuilder(
            future: _getAppInfo(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return Padding(
                  padding: const EdgeInsets.symmetric(vertical: 50.0),
                  child: Column(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        children: [
                          Column(
                            children: [
                              Text(
                                T.screens.healthInfo.configData,
                                style: const TextStyle(fontWeight: FontWeight.bold),
                              ),
                              const SizedBox(
                                height: 20,
                              ),
                              _buildConfigDataWidgets()
                            ],
                          ),

And _buildConfigDataWidgets() looks like this:

Widget _buildConfigDataWidgets() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(T.screens.healthInfo.data.configFile +
            (staticConfiguration.configFileExists
                ? T.screens.healthInfo.data.exists
                : T.screens.healthInfo.data.doesNotExist)),
        const SizedBox(
          height: 10,
        ),
        Text(T.screens.healthInfo.data.grpcHost + ': ' + staticConfiguration.grpcServerHost),
        const SizedBox(
          height: 10,
        ),
        Text(T.screens.healthInfo.data.grpcPort + ': ' + staticConfiguration.grpcServerPort.toString()),
        const SizedBox(
          height: 10,
        ),
        if (staticConfiguration.locale != null) ...[
          Text(T.screens.healthInfo.data.activeLanguageCode + ': ' + staticConfiguration.locale!.languageCode),
          const SizedBox(
            height: 10,
          ),
          if (staticConfiguration.locale!.countryCode != null)
            Text(T.screens.healthInfo.data.activeCountryCode + ': ' + staticConfiguration.locale!.countryCode!),
        ],
      ],
    );
  }

Upvotes: 1

Related Questions