Issue
I am new to flutter and I got a situation where I need to reuse data from an asynchronous call, after researching a bit I found out about the nice FutureBuilder Widget and I am using, it works great but I have this situation where I need the data from the future call in two different widgets like the code fragment below which causes the async call to be executed twice and I would like to avoid it.
How to avoid the two calls? What's the recommended approach in this case? I couldn't find a reference/recommendation for this situation.
body: Column(
children: [
Container(
height: 200,
child: FutureBuilder(
future: weight.findAll(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
this._seriesList = _plotWeightSeries(snapshot.data);
return _lineChart();
}
},
),
),
Expanded(
child: FutureBuilder(
future: weight.findAll(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return _getSlidableListView(snapshot.data);
}
},
),
),
],
),
Solution
Here is an example using Flutter Hooks and Riverpod.
I define a FutureProvider to fetch the weights from the server:
final weightsProvider = FutureProvider((ref) => findAllWeights());
Future<List<double>> findAllWeights() async {
print('FETCHING DATA'); // This gets run only once
final random = Random();
await Future.delayed(Duration(seconds: 2));
return List.generate(20, (index) => 50 + 20 * random.nextDouble());
}
And then, I use the result in both my WidgetOne to calculate the SUM and my WidgetTwo to calculate the AVERAGE. As you will see, the FETCHING DATA only happens once.
Full source code
import 'dart:math' show Random;
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/all.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
final weightsProvider = FutureProvider((ref) => findAllWeights());
Future<List<double>> findAllWeights() async {
print('FETCHING DATA'); // This gets run only once
final random = Random();
await Future.delayed(Duration(seconds: 2));
return List.generate(20, (index) => 50 + 20 * random.nextDouble());
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
WidgetOne(),
WidgetTwo(),
],
),
);
}
}
class WidgetOne extends HookWidget {
@override
Widget build(BuildContext context) {
final weights = useProvider(weightsProvider);
return Card(
child: Column(
children: [
Text('SUM of the weights'),
weights.when(
data: (data) => Text(data.reduce((a, b) => a + b).toString()),
loading: () => CircularProgressIndicator(),
error: (_, __) => Text('Something bad happended'),
),
],
),
);
}
}
class WidgetTwo extends HookWidget {
@override
Widget build(BuildContext context) {
final weights = useProvider(weightsProvider);
return Card(
child: Column(
children: [
Text('AVERAGE of the weights'),
weights.when(
data: (data) =>
Text((data.reduce((a, b) => a + b) / data.length).toString()),
loading: () => CircularProgressIndicator(),
error: (_, __) => Text('Something bad happended'),
),
],
),
);
}
}
I used Riverpod in this examples but there are other State Management, check here for a curated List of state management approaches.
Update for Riverpod 1.0
import 'dart:math' show Random;
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
const ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
final weightsProvider = FutureProvider((ref) => findAllWeights());
Future<List<double>> findAllWeights() async {
print('FETCHING DATA'); // This gets run only once
final random = Random();
await Future.delayed(const Duration(seconds: 2));
return List.generate(20, (index) => 50 + 20 * random.nextDouble());
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: const [
WidgetOne(),
WidgetTwo(),
],
),
);
}
}
class WidgetOne extends HookConsumerWidget {
const WidgetOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final weights = ref.watch(weightsProvider);
return Card(
child: Column(
children: [
const Text('SUM of the weights'),
weights.when(
data: (data) => Text(data.reduce((a, b) => a + b).toString()),
loading: () => const CircularProgressIndicator(),
error: (_, __) => const Text('Something bad happended'),
),
],
),
);
}
}
class WidgetTwo extends HookConsumerWidget {
const WidgetTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final weights = ref.watch(weightsProvider);
return Card(
child: Column(
children: [
const Text('AVERAGE of the weights'),
weights.when(
data: (data) =>
Text((data.reduce((a, b) => a + b) / data.length).toString()),
loading: () => const CircularProgressIndicator(),
error: (_, __) => const Text('Something bad happended'),
),
],
),
);
}
}
Answered By - Thierry
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.