user11065582
user11065582

Reputation:

How to fix FutureBuilder open multiple times error?

these my two classes(two pages). these two classes open multiple times. I put debug point in futurebuilder in two classes. debug point running,

  1. MainCategory page and got to the next page
  2. SubCategory page and again running MainCategory page(previous page) futurebuilder and again running MainCategory page futurebuilder
  3. navigate subcategory page to third page running subcategory page and main category page

I upload my two classes to GitHub and please let me know what the issue is.

MainCategory code: https://github.com/bhanuka96/ios_login/blob/master/MainCategory.dart SubCategory code: https://github.com/bhanuka96/ios_login/blob/master/subCategory.dart

Upvotes: 3

Views: 5493

Answers (3)

Hellomik U
Hellomik U

Reputation: 563

You can create new Widget and pass Function to returnFuture as

() {
 return YourFuture;
} 
import 'dart:developer';
import 'package:flutter/material.dart';

class MyFutureBuilder<T> extends StatefulWidget {
  final Future<T> Function() returnFuture;
  final AsyncWidgetBuilder<T> builder;
  final T initialData;
  MyFutureBuilder({
    this.returnFuture,
    @required this.builder,
    this.initialData,
    Key key,
  }) : super(key: key);

  @override
  _MyFutureBuilderState<T> createState() => _MyFutureBuilderState<T>();
}

class _MyFutureBuilderState<T> extends State<MyFutureBuilder<T>> {
  bool isLoading = false;
  Future<T> future;

  @override
  void initState() {
    super.initState();
    future = widget.returnFuture();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      builder: widget.builder,
      initialData: widget.initialData,
      future: future,
    );
  }
}

Example

MyFutureBuilder<List<User>>(
                                  returnFuture: () {
                                    return moderatorUserProvider
                                        .getExecutorsAsModeratorByIds(val.users,
                                            save: true);
                                  },
                                  builder: (cont, asyncData) {

                                    if (asyncData.connectionState !=
                                        ConnectionState.done) {
                                      return Center(
                                        child: MyCircularProgressIndicator(
                                          color: ModeratorColor.executors.color,
                                        ),
                                      );
                                    }
                                    return Column(
                                        children: asyncData.data
                                            .map(
                                              (singlExecutor) =>
                                                  ChooseInfoButton(
                                                title:
                                                    '${singlExecutor.firstName} ${singlExecutor.secondName}',
                                                subTitle: 'Business analyst',
                                                middleText: '4.000 NOK',
                                                subMiddleText: 'full time',
                                                label: 'test period',
                                                subLabel: '1.5 month',
                                                imageUrl:
                                                    assetsUrl + 'download.jpeg',
                                                onTap: () {
                                                  Navigator.of(context).push(
                                                    MaterialPageRoute(
                                                      builder: (_) =>
                                                          ModeratorExecutorEditPage(),
                                                    ),
                                                  );
                                                },
                                              ),
                                            )
                                            .toList());
                                  },
                                )
    ```

Upvotes: 0

Frank Treacy
Frank Treacy

Reputation: 3706

If you happen to use Provider, here's (in my opinion) a clearer alternative based on your question:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureProvider<List<String>>(
      create: (_) => MyApiHelper.getMyList(),
      child: Consumer<List<String>>(
        builder: (_, list, __) {
          if (list == null) return CircularProgressIndicator();
          return ListView.builder(
            itemCount: list.length,
            itemBuilder: (_, index) {
              //show your list item row here...
            },
          );
        };
      ),
    );
  }
}

This can also be achieved of course as a StatefulWidget as suggested by the other answer, or even with flutter_hooks as explained in Why is my Future/Async Called Multiple Times?

Upvotes: 0

pso
pso

Reputation: 889

As stated in the documentation, you should not fetch the Future for the Futurebuilder during the widget's build event.

https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html

The future must have been obtained earlier, e.g. during State.initState, State.didUpdateConfig, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.

So, try to move your call to getRegister method outside the build method and replace it with the returned Future value.

For example, below I have a class that returns a Future value which will be consumed with the help of FutureBuilder.

class MyApiHelper{

  static Future<List<String>> getMyList() async {
    // your implementation to make server calls
    return List<String>();
  }
}

Now, inside your widget, you will have something like this:

class _MyHomePageState extends State<MyHomePage> {

  Future<List<String>> _myList;

  @override
  void initState() {
    super.initState();

    _myList = MyApiHelper.getMyList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: FutureBuilder(
      future: _myList,
      builder: (_, AsyncSnapshot<List<String>> snapLs) {
        if(!snapLs.hasData) return CircularProgressIndicator();

        return ListView.builder(
          itemCount: snapLs.data.length,
          itemBuilder: (_, index) {
            //show your list item row here...
          },
        );
      },
    ));
  }
}

As shown above, the Future is fetched in the initState function and used inside the build method and used by FutureBuilder.

I hope this was helpful. Thanks.

Upvotes: 15

Related Questions