I have two widgets that both use the same Future to display CircularProgressIndicators and then show the widget once the future completes. But the CircularProgressIndicators do not animate, so they are just small squares. They animate the very first time after a full compile, but they do not animate ever again, not even after an app refresh. I have tried with other animating widgets to confirm, and they are indeed static:
@override
Widget build(BuildContext context) {
_mapLoadedNotifier = ref.read(mapLoadedProvider.notifier);
_mapLoaded = ref.watch(mapLoadedProvider);
theVm = ref.watch(searchPageVmProvider);
return Row(children: [ _mapOverlay(),_mapWidget(),]);
}
Widget _mapOverlay() {
return theVm.when(
data: (store) {
vm = store;
if (!vm!.hasSearched) {
vm!.isMapLoading = true;
}
vm!.ref = ref;
return SearchFormBuilder(
initState: vm!.initState,
model: Search(search: SearchByTermSuggestion('')),
builder: (context, formModel, child) {
return Container(
padding: PAD_M_TOP,
alignment: Alignment.topLeft,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: PAD_M),
width: sizingInfo.maxWidthS,
color: Colors.transparent,
child: VpCombinationAutocompleteField(
formControl: formModel.searchControl,
labelText: '',
hintText: 'Search',
widgetRef: vm!.ref!,
widgetOnSuggestionSelected:
(Suggestion suggestion) =>
suggestion.onSelected())));
});
},
error: (error, s) => Text(error.toString()),
loading: () {
return const Center(
child: SizedBox(
height: 30,
width: 30,
child: Center(child: CircularProgressIndicator())),
);
});
}
Widget _mapWidget() {
return FutureBuilder<SearchPageVm>(
future: ref.watch(searchPageVmProvider.future),
builder: ((context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: SizedBox(
height: 30,
width: 30,
child: Center(child: CircularProgressIndicator())),
);
}
vm = snapshot.data;
return StreamBuilder<dynamic>(
stream: vm!.map$,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
vm!.vpMap = snapshot.data;
if (!vm!.hasSearched) {
vm!.isMapLoading = true;
}
vm!.ref = ref;
}
return _googleMap(vm!);
});
}));
}
When I remove the StreamBuilder
, they both animate correctly. This does not appear to be a riverPod issue, since I have tried plain Flutter FutureBuilders and it has the same issue. I’ve tried so many alternatives. The StreamBuilder stops the FutureBuilders CircularProgressIndicators from animating. Why?
It is the same issue as here:
Flutter CircularProgressIndicator() animation is not working inside Riverpod`s ref.watch().when
The provider:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:vepo/src/_common/extensions/vegan_item_establishments.dart';
import '../../../_common/constants/presentation/colors.dart';
import '../../../_common/enums/vegan_item_type.dart';
import '../../../_common/services/theme_mode.dart';
import '../../../_common/services/user.dart';
import '../../../application/local/form_capabilities.dart';
import '../../../application/local/map_data/map.dart';
import '../../../application/local/suggestion/search_by_term/search_by_term_suggestion.dart';
import '../../../application/search_params/vegan_item/vegan_item_search_params.dart';
import '../../../domain/vegan_item_establishment/_common/vegan_item_establishment.dart';
import 'search.dart';
import 'search_results_provider.dart';
final searchPageVmProvider = FutureProvider<SearchPageVm>((ref) async {
final userWithLocation = await ref.watch(userWithLocationProvider.future);
final vm = SearchPageVm(
userWithLocation: userWithLocation,
);
vm.search();
return vm;
});
class SearchPageVm {
bool hasSearched = false;
late GoogleMap? googleMapWidget;
bool isMapLoading = true;
VpMap? vpMap;
WidgetRef? ref;
ThemeMode? themeMode;
Color? statusBarColor;
String? searchText;
Stream<VpMap>? map$;
late final StreamController<VpMap> mapController =
StreamController.broadcast();
late UserWithLocation _userWithLocation;
Set<Marker> markers = <Marker>{};
Stream<String?>? searchTerm;
GoogleMapController? googleMapController;
SearchPageVm({required UserWithLocation userWithLocation}) {
map$ = mapController.stream;
_userWithLocation = userWithLocation;
}
@override
Search get model => Search(search: SearchByTermSuggestion(''));
Position get userPosition => _userWithLocation.userPosition;
/// Add an array of search results to the map
void addResults(Iterable<VgnItmEst> searchResults) {
mapController.add(VpMap(
position: _userWithLocation.userPosition,
markers: searchResults.markers));
_animateCamera(searchResults);
}
@override
void initState(BuildContext context, SearchForm formModel, [dynamic args]) {
if (!hasSearched) {
search();
hasSearched = true;
}
themeMode = ref?.read(themeModeServiceProvider);
statusBarColor = themeMode == ThemeMode.light
? BACKGROUND_COLOR
: DARK_THEME_BACKGROUND_COLOR_LIGHT;
}
/// Search for results and add them to the map
Future<List<VgnItmEst>?> search(
{String searchTerm = '', VgnItmType? category}) async {
final searchResults =
await _search(searchTerm: searchTerm, category: category);
addResults(searchResults ?? []);
return searchResults;
}
void _animateCamera(Iterable<VgnItmEst> searchResults) {
googleMapController?.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
searchResults.first.establishment!.location!.latitude,
searchResults.first.establishment!.location!.longitude),
zoom: 13)));
}
Future<List<VgnItmEst>?> _search(
{required String searchTerm, VgnItmType? category}) async {
final result = await ref?.watch(searchResultsProvider(VgnItmSearchParams(
searchTerm: searchTerm,
page: 1,
pageSize: 10,
vgnItmDiscriminators: category != null ? [category.name] : null))
.future);
return result;
}
}
final searchResultsProvider =
FutureProvider.family<List<VgnItmEst>, VgnItmSearchParams>(
(ref, searchParams) async {
List<VgnItmEst>? searchResults;
final allVgnItmsGooglePlacesRepo = ref.read(allVgnItmEstsRepoProvider);
searchResults = (await allVgnItmsGooglePlacesRepo.search(searchParams))?.data;
return searchResults!;
});
2
Answers
The way you handle different state of your
FutureBuilder
is wrong, also when you useFutureBuilder
, you should useref.read
, you need to change your_mapWidget
to this:Just Make your own progress dialog for just do single change and change will reflect whole code.