how to perform a single execution of a future?

Viewed 114

What I am trying to do is that by starting the app, make a request to a server and all information is saved in the database, for that I use a FutureBuilder that does the whole process, once finished it starts the application as normal.

The problem is that the application executes my future synchronization more than twice, causing errors with the insert to database. the following code is a basic example of what i am trying to do and the result i am getting.

main.dart

void main() => runApp(MyMaterialApp());

class MyMaterialApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Running MyMaterialApp');
    return MaterialApp(
      home: SplashLoad(),
    );
  }
}

class SplashLoad extends StatefulWidget {

  @override
  _SplashLoadState createState() => _SplashLoadState();
}

class _SplashLoadState extends State<SplashLoad> {
  final apiSimulation = new ApiSimulation();

  @override
  Widget build(BuildContext context) {
    print('SplashScreen');
    return Scaffold(
      body: Container(
        child: Center(
          child: FutureBuilder(
            future: apiSimulation.sincronizacion(),
            builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
              if (snapshot.hasData) {
                return Text('load finished data:${snapshot.data}');
              }
              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

ApiSimulation

class ApiSimulation {
  int number = 0;
  Future<int> synchronization()async{
    print('INIT SYNCHRONIZATION');
    await Future.delayed(Duration(seconds: 2));
    final data = await _getData();
    return data;
  }

  Future<int> _getData() async{
    number++;
    print('running getData$number');
    await Future.delayed(Duration(seconds: 2));
    return number;
  }
}

console result

I/flutter (32404): Running MyMaterialApp
I/flutter (32404): SplashScreen
I/flutter (32404): INIT SYNCHRONIZATION
I/flutter (32404): Running MyMaterialApp
I/flutter (32404): SplashScreen
I/flutter (32404): INIT SYNCHRONIZATION
I/flutter (32404): running getData1
I/flutter (32404): running getData2

Sometimes reaching 4 in the value of the number

1 Answers

This is what the document says

The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, 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.

class _SplashLoadState extends State<SplashLoad> {
  late final Future simFuture;
  @override
  void initState() {
    super.initState();
    simFuture= ApiSimulation().sincronizacion(); //initiate your future here
  }

Within your future builder use

FutureBuilder(
   future: simFuture,
   builder: (ctx,snap){..},
)
Related