Skip to content

Commit 3371c12

Browse files
fix infinite scroll list console errors (#46)
* fix infinite scroll list console errors * fix other errors and clean some code * improve activity style when username is displayed * improve widget trees * fix some consle errors * fix not entirely the location provider * try to fix location * fix imports * fix
1 parent 2f69a7e commit 3371c12

26 files changed

Lines changed: 327 additions & 323 deletions

lib/presentation/common/activity/view_model/activity_item_view_model.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import 'package:flutter/material.dart';
22
import 'package:hooks_riverpod/hooks_riverpod.dart';
3-
import '../../user/view_model/profile_picture_view_model.dart';
4-
import '../../../my_activities/view_model/activity_list_view_model.dart';
3+
54
import '../../../../data/repositories/activity_repository_impl.dart';
65
import '../../../../domain/entities/activity.dart';
76
import '../../../../main.dart';
87
import '../../../my_activities/screens/activity_details_screen.dart';
8+
import '../../../my_activities/view_model/activity_list_view_model.dart';
9+
import '../../user/view_model/profile_picture_view_model.dart';
910
import 'state/activity_item_state.dart';
1011

1112
/// Provider for the activity item view model.

lib/presentation/common/activity/widgets/activity_comments.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import 'package:comment_box/comment/comment.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
66
import 'package:hooks_riverpod/hooks_riverpod.dart';
7-
import '../../user/view_model/profile_picture_view_model.dart';
87

98
import '../../../../core/utils/storage_utils.dart';
109
import '../../../../domain/entities/activity.dart';
1110
import '../../../../domain/entities/activity_comment.dart';
1211
import '../../../../domain/entities/user.dart';
1312
import '../../core/utils/color_utils.dart';
1413
import '../../core/utils/user_utils.dart';
14+
import '../../user/view_model/profile_picture_view_model.dart';
1515
import '../view_model/activity_item_comments_view_model.dart';
1616
import '../view_model/activity_item_view_model.dart';
1717

lib/presentation/common/activity/widgets/activity_item.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class ActivityItem extends HookConsumerWidget {
3333
final state = ref.watch(activityItemViewModelProvider(activity.id));
3434

3535
final List<Color> colors = ColorUtils.generateColorTupleFromIndex(index);
36+
final titleColor = colors.first;
37+
3638
final startColor = colors.first;
3739
final endColor = colors.last;
3840
const double borderRadius = 24;
@@ -111,7 +113,7 @@ class ActivityItem extends HookConsumerWidget {
111113
ActivityItemDetails(
112114
displayUserName: displayUserName,
113115
activity: activity,
114-
color: startColor),
116+
color: titleColor),
115117
],
116118
),
117119
),

lib/presentation/common/activity/widgets/activity_item_details.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ class ActivityItemDetails extends StatelessWidget {
8080
],
8181
),
8282
Padding(
83-
padding: EdgeInsets.only(
84-
left: displayUserName ? 30 : 0,
85-
bottom: displayUserName ? 30 : 0),
83+
padding: EdgeInsets.only(left: 0, bottom: displayUserName ? 30 : 0),
8684
child: buildActivityDetails(
8785
context, appLocalizations, formattedDate, formattedTime),
8886
),

lib/presentation/common/activity/widgets/activity_list.dart

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ class ActivityList extends HookConsumerWidget {
6767
? state.groupedActivities
6868
: groupActivitiesByMonth(activities);
6969

70-
return Expanded(
71-
child: InfiniteScrollList(
70+
return InfiniteScrollList(
7271
listId: id,
7372
initialData: groupedActivities,
7473
total: total,
@@ -109,41 +108,39 @@ class ActivityList extends HookConsumerWidget {
109108
previousMonthsTotal += groupedActivities[i].length as int;
110109
}
111110

112-
return Expanded(
113-
child: Theme(
114-
data: ThemeData(
115-
expansionTileTheme: ExpansionTileThemeData(
116-
tilePadding: EdgeInsets.zero,
117-
iconColor: ColorUtils.black,
118-
textColor: ColorUtils.black,
119-
childrenPadding: EdgeInsets.zero,
120-
shape: const RoundedRectangleBorder(
121-
side: BorderSide.none,
122-
borderRadius: BorderRadius.zero,
123-
),
111+
return Theme(
112+
data: ThemeData(
113+
expansionTileTheme: ExpansionTileThemeData(
114+
tilePadding: EdgeInsets.zero,
115+
iconColor: ColorUtils.black,
116+
textColor: ColorUtils.black,
117+
childrenPadding: EdgeInsets.zero,
118+
shape: const RoundedRectangleBorder(
119+
side: BorderSide.none,
120+
borderRadius: BorderRadius.zero,
121+
),
122+
),
123+
),
124+
child: ExpansionTile(
125+
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
126+
title: Text(
127+
'${_getMonthName(monthActivities.first.startDatetime, AppLocalizations.of(context)!.localeName)} ${monthActivities.first.startDatetime.year}',
128+
style: const TextStyle(
129+
fontWeight: FontWeight.bold,
124130
),
125131
),
126-
child: ExpansionTile(
127-
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
128-
title: Text(
129-
'${_getMonthName(monthActivities.first.startDatetime, AppLocalizations.of(context)!.localeName)} ${monthActivities.first.startDatetime.year}',
130-
style: const TextStyle(
131-
fontWeight: FontWeight.bold,
132-
),
133-
),
134-
initiallyExpanded: true,
135-
children:
136-
monthActivities.asMap().entries.map<Widget>((entry) {
137-
final index = previousMonthsTotal + entry.key;
138-
final activity = entry.value;
139-
return ActivityItem(
140-
index: index as int,
141-
activity: activity,
142-
displayUserName: displayUserName,
143-
canOpenActivity: canOpenActivity,
144-
);
145-
}).toList())));
132+
initiallyExpanded: true,
133+
children: monthActivities.asMap().entries.map<Widget>((entry) {
134+
final index = previousMonthsTotal + entry.key;
135+
final activity = entry.value;
136+
return ActivityItem(
137+
index: index as int,
138+
activity: activity,
139+
displayUserName: displayUserName,
140+
canOpenActivity: canOpenActivity,
141+
);
142+
}).toList()));
146143
},
147-
));
144+
);
148145
}
149146
}

lib/presentation/common/core/utils/activity_utils.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,25 @@ class ActivityUtils {
142142

143143
static Future<void> _updateActivityList(Ref<Object?> ref, String listType,
144144
Activity updatedActivity, ActivityUpdateActionEnum action) async {
145-
var data = ref.read(infiniteScrollListViewModelProvider(listType)).data;
146-
147-
List<List<Activity>> newData = [];
148-
149-
if (action == ActivityUpdateActionEnum.edit) {
150-
newData = ActivityUtils.replaceActivity(
151-
data as List<List<Activity>>, updatedActivity);
152-
} else if (action == ActivityUpdateActionEnum.remove) {
153-
newData = ActivityUtils.deleteActivity(
154-
data as List<List<Activity>>, updatedActivity);
155-
} else if (action == ActivityUpdateActionEnum.add) {
156-
newData = ActivityUtils.prependActivity(
157-
data as List<List<Activity>>, updatedActivity);
145+
var data = ref
146+
.read(infiniteScrollListViewModelProvider(listType))
147+
.data
148+
.cast<List<Activity>>();
149+
150+
if (data.isNotEmpty) {
151+
List<List<Activity>> newData = [];
152+
153+
if (action == ActivityUpdateActionEnum.edit) {
154+
newData = ActivityUtils.replaceActivity(data, updatedActivity);
155+
} else if (action == ActivityUpdateActionEnum.remove) {
156+
newData = ActivityUtils.deleteActivity(data, updatedActivity);
157+
} else if (action == ActivityUpdateActionEnum.add) {
158+
newData = ActivityUtils.prependActivity(data, updatedActivity);
159+
}
160+
161+
ref
162+
.read(infiniteScrollListViewModelProvider(listType).notifier)
163+
.replaceData(newData);
158164
}
159-
160-
ref
161-
.read(infiniteScrollListViewModelProvider(listType).notifier)
162-
.replaceData(newData);
163165
}
164166
}

lib/presentation/common/core/widgets/infinite_scroll_list.dart

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,23 @@ class InfiniteScrollList extends HookConsumerWidget {
102102
state.data.isNotEmpty ? '' : provider.setData(initialData);
103103
});
104104

105-
return state.isLoading
106-
? buildLoadingIndicator()
107-
: ListView.builder(
108-
key: _listKey,
109-
controller: scrollController,
110-
itemCount:
111-
state.data.length + (hasMoreData(state.data, total) ? 1 : 0),
112-
itemBuilder: (context, index) {
113-
if (index < state.data.length) {
114-
return itemBuildFunction(context, state.data, index);
115-
} else {
116-
return state.isLoading
117-
? buildLoadingIndicator()
118-
: buildLoadMoreButton(context, state, provider);
119-
}
120-
},
121-
);
105+
return Expanded(
106+
child: state.isLoading
107+
? buildLoadingIndicator()
108+
: ListView.builder(
109+
key: _listKey,
110+
controller: scrollController,
111+
itemCount: state.data.length +
112+
(hasMoreData(state.data, total) ? 1 : 0),
113+
itemBuilder: (context, index) {
114+
if (index < state.data.length) {
115+
return itemBuildFunction(context, state.data, index);
116+
} else {
117+
return state.isLoading
118+
? buildLoadingIndicator()
119+
: buildLoadMoreButton(context, state, provider);
120+
}
121+
},
122+
));
122123
}
123124
}

lib/presentation/common/core/widgets/share_map_button.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ShareMapButton extends HookConsumerWidget {
5151
}
5252

5353
return FloatingActionButton(
54+
heroTag: 'share_button',
5455
onPressed: () async {
5556
try {
5657
Uint8List? image = await ImageUtils.captureWidgetToImage(boundaryKey);

lib/presentation/common/location/view_model/location_view_model.dart

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'dart:async';
22

3-
import 'package:flutter/widgets.dart';
43
import 'package:flutter_map/flutter_map.dart';
54
import 'package:geolocator/geolocator.dart';
65
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -11,16 +10,15 @@ import '../../metrics/view_model/metrics_view_model.dart';
1110
import '../../timer/viewmodel/timer_view_model.dart';
1211
import 'state/location_state.dart';
1312

14-
/// Provider for the [LocationViewModel].
1513
final locationViewModelProvider =
16-
StateNotifierProvider.autoDispose<LocationViewModel, LocationState>(
14+
StateNotifierProvider<LocationViewModel, LocationState>(
1715
(ref) => LocationViewModel(ref),
1816
);
1917

2018
/// View model for managing location-related functionality.
2119
class LocationViewModel extends StateNotifier<LocationState> {
2220
final Ref ref;
23-
final MapController mapController = MapController();
21+
MapController? mapController = MapController();
2422
StreamSubscription<Position>? _positionStream;
2523

2624
/// Creates a [LocationViewModel] instance.
@@ -29,9 +27,9 @@ class LocationViewModel extends StateNotifier<LocationState> {
2927
LocationViewModel(this.ref) : super(LocationState.initial());
3028

3129
@override
32-
void dispose() {
30+
Future<void> dispose() async {
31+
await cancelLocationStream();
3332
super.dispose();
34-
cancelLocationStream();
3533
}
3634

3735
/// Starts getting the user's location updates.
@@ -48,13 +46,13 @@ class LocationViewModel extends StateNotifier<LocationState> {
4846
}
4947
_positionStream ??=
5048
Geolocator.getPositionStream().listen((Position position) {
51-
if (mounted) {
52-
WidgetsBinding.instance.addPostFrameCallback((_) {
53-
mapController.move(
49+
if (mounted && _positionStream != null && mapController != null) {
50+
if (mounted && _positionStream != null && mapController != null) {
51+
mapController?.move(
5452
LatLng(position.latitude, position.longitude),
5553
17,
5654
);
57-
});
55+
}
5856

5957
final timerProvider = ref.read(timerViewModelProvider.notifier);
6058
if (timerProvider.isTimerRunning() && timerProvider.hasTimerStarted()) {
@@ -102,11 +100,10 @@ class LocationViewModel extends StateNotifier<LocationState> {
102100
}
103101

104102
/// Cancels the location stream and cleans up resources.
105-
void cancelLocationStream() async {
106-
await _positionStream?.cancel().whenComplete(() {
107-
_positionStream = null;
108-
state = state.copyWith(currentPosition: null);
109-
});
103+
Future<void> cancelLocationStream() async {
104+
await _positionStream?.cancel();
105+
_positionStream = null;
106+
state = state.copyWith(currentPosition: null);
110107
}
111108

112109
/// Checks if the location stream is currently paused.

lib/presentation/common/location/widgets/current_location_map.dart

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_hooks/flutter_hooks.dart';
23
import 'package:flutter_map/flutter_map.dart';
34
import 'package:hooks_riverpod/hooks_riverpod.dart';
45
import 'package:latlong2/latlong.dart';
56

67
import '../../core/utils/color_utils.dart';
8+
import '../../core/utils/ui_utils.dart';
79
import '../view_model/location_view_model.dart';
810
import 'location_map.dart';
911

1012
/// Widget that displays the current location on a map.
1113
class CurrentLocationMap extends HookConsumerWidget {
12-
const CurrentLocationMap({super.key});
14+
CurrentLocationMap({super.key});
15+
16+
final dataFutureProvider = FutureProvider<void>((ref) async {
17+
final provider = ref.read(locationViewModelProvider.notifier);
18+
return await provider.startGettingLocation();
19+
});
1320

1421
@override
1522
Widget build(BuildContext context, WidgetRef ref) {
16-
final state = ref.watch(locationViewModelProvider);
1723
final provider = ref.read(locationViewModelProvider.notifier);
24+
final state = ref.watch(locationViewModelProvider);
25+
26+
var futureProvider = ref.watch(dataFutureProvider);
1827

1928
final points = provider.savedPositionsLatLng();
2029

@@ -58,6 +67,24 @@ class CurrentLocationMap extends HookConsumerWidget {
5867
);
5968
}
6069

61-
return LocationMap(points: points, markers: markers);
70+
useEffect(() {
71+
return () async {
72+
await provider.cancelLocationStream();
73+
};
74+
}, []);
75+
76+
return futureProvider.when(data: (total) {
77+
return Expanded(
78+
child: LocationMap(
79+
points: points,
80+
markers: markers,
81+
currentPosition: LatLng(currentLatitude, currentLongitude),
82+
mapController: provider.mapController ?? MapController(),
83+
));
84+
}, loading: () {
85+
return Expanded(child: Center(child: UIUtils.loader));
86+
}, error: (error, stackTrace) {
87+
return Text('$error');
88+
});
6289
}
6390
}

0 commit comments

Comments
 (0)