Skip to content

Commit

Permalink
fix infinite scroll list console errors (#46)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
BenjaminCanape authored Feb 12, 2024
1 parent 2f69a7e commit 3371c12
Show file tree
Hide file tree
Showing 26 changed files with 327 additions and 323 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../user/view_model/profile_picture_view_model.dart';
import '../../../my_activities/view_model/activity_list_view_model.dart';

import '../../../../data/repositories/activity_repository_impl.dart';
import '../../../../domain/entities/activity.dart';
import '../../../../main.dart';
import '../../../my_activities/screens/activity_details_screen.dart';
import '../../../my_activities/view_model/activity_list_view_model.dart';
import '../../user/view_model/profile_picture_view_model.dart';
import 'state/activity_item_state.dart';

/// Provider for the activity item view model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import 'package:comment_box/comment/comment.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../user/view_model/profile_picture_view_model.dart';

import '../../../../core/utils/storage_utils.dart';
import '../../../../domain/entities/activity.dart';
import '../../../../domain/entities/activity_comment.dart';
import '../../../../domain/entities/user.dart';
import '../../core/utils/color_utils.dart';
import '../../core/utils/user_utils.dart';
import '../../user/view_model/profile_picture_view_model.dart';
import '../view_model/activity_item_comments_view_model.dart';
import '../view_model/activity_item_view_model.dart';

Expand Down
4 changes: 3 additions & 1 deletion lib/presentation/common/activity/widgets/activity_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class ActivityItem extends HookConsumerWidget {
final state = ref.watch(activityItemViewModelProvider(activity.id));

final List<Color> colors = ColorUtils.generateColorTupleFromIndex(index);
final titleColor = colors.first;

final startColor = colors.first;
final endColor = colors.last;
const double borderRadius = 24;
Expand Down Expand Up @@ -111,7 +113,7 @@ class ActivityItem extends HookConsumerWidget {
ActivityItemDetails(
displayUserName: displayUserName,
activity: activity,
color: startColor),
color: titleColor),
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ class ActivityItemDetails extends StatelessWidget {
],
),
Padding(
padding: EdgeInsets.only(
left: displayUserName ? 30 : 0,
bottom: displayUserName ? 30 : 0),
padding: EdgeInsets.only(left: 0, bottom: displayUserName ? 30 : 0),
child: buildActivityDetails(
context, appLocalizations, formattedDate, formattedTime),
),
Expand Down
67 changes: 32 additions & 35 deletions lib/presentation/common/activity/widgets/activity_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ class ActivityList extends HookConsumerWidget {
? state.groupedActivities
: groupActivitiesByMonth(activities);

return Expanded(
child: InfiniteScrollList(
return InfiniteScrollList(
listId: id,
initialData: groupedActivities,
total: total,
Expand Down Expand Up @@ -109,41 +108,39 @@ class ActivityList extends HookConsumerWidget {
previousMonthsTotal += groupedActivities[i].length as int;
}

return Expanded(
child: Theme(
data: ThemeData(
expansionTileTheme: ExpansionTileThemeData(
tilePadding: EdgeInsets.zero,
iconColor: ColorUtils.black,
textColor: ColorUtils.black,
childrenPadding: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
side: BorderSide.none,
borderRadius: BorderRadius.zero,
),
return Theme(
data: ThemeData(
expansionTileTheme: ExpansionTileThemeData(
tilePadding: EdgeInsets.zero,
iconColor: ColorUtils.black,
textColor: ColorUtils.black,
childrenPadding: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
side: BorderSide.none,
borderRadius: BorderRadius.zero,
),
),
),
child: ExpansionTile(
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
title: Text(
'${_getMonthName(monthActivities.first.startDatetime, AppLocalizations.of(context)!.localeName)} ${monthActivities.first.startDatetime.year}',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
child: ExpansionTile(
tilePadding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
title: Text(
'${_getMonthName(monthActivities.first.startDatetime, AppLocalizations.of(context)!.localeName)} ${monthActivities.first.startDatetime.year}',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
initiallyExpanded: true,
children:
monthActivities.asMap().entries.map<Widget>((entry) {
final index = previousMonthsTotal + entry.key;
final activity = entry.value;
return ActivityItem(
index: index as int,
activity: activity,
displayUserName: displayUserName,
canOpenActivity: canOpenActivity,
);
}).toList())));
initiallyExpanded: true,
children: monthActivities.asMap().entries.map<Widget>((entry) {
final index = previousMonthsTotal + entry.key;
final activity = entry.value;
return ActivityItem(
index: index as int,
activity: activity,
displayUserName: displayUserName,
canOpenActivity: canOpenActivity,
);
}).toList()));
},
));
);
}
}
36 changes: 19 additions & 17 deletions lib/presentation/common/core/utils/activity_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,23 +142,25 @@ class ActivityUtils {

static Future<void> _updateActivityList(Ref<Object?> ref, String listType,
Activity updatedActivity, ActivityUpdateActionEnum action) async {
var data = ref.read(infiniteScrollListViewModelProvider(listType)).data;

List<List<Activity>> newData = [];

if (action == ActivityUpdateActionEnum.edit) {
newData = ActivityUtils.replaceActivity(
data as List<List<Activity>>, updatedActivity);
} else if (action == ActivityUpdateActionEnum.remove) {
newData = ActivityUtils.deleteActivity(
data as List<List<Activity>>, updatedActivity);
} else if (action == ActivityUpdateActionEnum.add) {
newData = ActivityUtils.prependActivity(
data as List<List<Activity>>, updatedActivity);
var data = ref
.read(infiniteScrollListViewModelProvider(listType))
.data
.cast<List<Activity>>();

if (data.isNotEmpty) {
List<List<Activity>> newData = [];

if (action == ActivityUpdateActionEnum.edit) {
newData = ActivityUtils.replaceActivity(data, updatedActivity);
} else if (action == ActivityUpdateActionEnum.remove) {
newData = ActivityUtils.deleteActivity(data, updatedActivity);
} else if (action == ActivityUpdateActionEnum.add) {
newData = ActivityUtils.prependActivity(data, updatedActivity);
}

ref
.read(infiniteScrollListViewModelProvider(listType).notifier)
.replaceData(newData);
}

ref
.read(infiniteScrollListViewModelProvider(listType).notifier)
.replaceData(newData);
}
}
35 changes: 18 additions & 17 deletions lib/presentation/common/core/widgets/infinite_scroll_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,23 @@ class InfiniteScrollList extends HookConsumerWidget {
state.data.isNotEmpty ? '' : provider.setData(initialData);
});

return state.isLoading
? buildLoadingIndicator()
: ListView.builder(
key: _listKey,
controller: scrollController,
itemCount:
state.data.length + (hasMoreData(state.data, total) ? 1 : 0),
itemBuilder: (context, index) {
if (index < state.data.length) {
return itemBuildFunction(context, state.data, index);
} else {
return state.isLoading
? buildLoadingIndicator()
: buildLoadMoreButton(context, state, provider);
}
},
);
return Expanded(
child: state.isLoading
? buildLoadingIndicator()
: ListView.builder(
key: _listKey,
controller: scrollController,
itemCount: state.data.length +
(hasMoreData(state.data, total) ? 1 : 0),
itemBuilder: (context, index) {
if (index < state.data.length) {
return itemBuildFunction(context, state.data, index);
} else {
return state.isLoading
? buildLoadingIndicator()
: buildLoadMoreButton(context, state, provider);
}
},
));
}
}
1 change: 1 addition & 0 deletions lib/presentation/common/core/widgets/share_map_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ShareMapButton extends HookConsumerWidget {
}

return FloatingActionButton(
heroTag: 'share_button',
onPressed: () async {
try {
Uint8List? image = await ImageUtils.captureWidgetToImage(boundaryKey);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
Expand All @@ -11,16 +10,15 @@ import '../../metrics/view_model/metrics_view_model.dart';
import '../../timer/viewmodel/timer_view_model.dart';
import 'state/location_state.dart';

/// Provider for the [LocationViewModel].
final locationViewModelProvider =
StateNotifierProvider.autoDispose<LocationViewModel, LocationState>(
StateNotifierProvider<LocationViewModel, LocationState>(
(ref) => LocationViewModel(ref),
);

/// View model for managing location-related functionality.
class LocationViewModel extends StateNotifier<LocationState> {
final Ref ref;
final MapController mapController = MapController();
MapController? mapController = MapController();
StreamSubscription<Position>? _positionStream;

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

@override
void dispose() {
Future<void> dispose() async {
await cancelLocationStream();
super.dispose();
cancelLocationStream();
}

/// Starts getting the user's location updates.
Expand All @@ -48,13 +46,13 @@ class LocationViewModel extends StateNotifier<LocationState> {
}
_positionStream ??=
Geolocator.getPositionStream().listen((Position position) {
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
mapController.move(
if (mounted && _positionStream != null && mapController != null) {
if (mounted && _positionStream != null && mapController != null) {
mapController?.move(
LatLng(position.latitude, position.longitude),
17,
);
});
}

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

/// Cancels the location stream and cleans up resources.
void cancelLocationStream() async {
await _positionStream?.cancel().whenComplete(() {
_positionStream = null;
state = state.copyWith(currentPosition: null);
});
Future<void> cancelLocationStream() async {
await _positionStream?.cancel();
_positionStream = null;
state = state.copyWith(currentPosition: null);
}

/// Checks if the location stream is currently paused.
Expand Down
33 changes: 30 additions & 3 deletions lib/presentation/common/location/widgets/current_location_map.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:latlong2/latlong.dart';

import '../../core/utils/color_utils.dart';
import '../../core/utils/ui_utils.dart';
import '../view_model/location_view_model.dart';
import 'location_map.dart';

/// Widget that displays the current location on a map.
class CurrentLocationMap extends HookConsumerWidget {
const CurrentLocationMap({super.key});
CurrentLocationMap({super.key});

final dataFutureProvider = FutureProvider<void>((ref) async {
final provider = ref.read(locationViewModelProvider.notifier);
return await provider.startGettingLocation();
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(locationViewModelProvider);
final provider = ref.read(locationViewModelProvider.notifier);
final state = ref.watch(locationViewModelProvider);

var futureProvider = ref.watch(dataFutureProvider);

final points = provider.savedPositionsLatLng();

Expand Down Expand Up @@ -58,6 +67,24 @@ class CurrentLocationMap extends HookConsumerWidget {
);
}

return LocationMap(points: points, markers: markers);
useEffect(() {
return () async {
await provider.cancelLocationStream();
};
}, []);

return futureProvider.when(data: (total) {
return Expanded(
child: LocationMap(
points: points,
markers: markers,
currentPosition: LatLng(currentLatitude, currentLongitude),
mapController: provider.mapController ?? MapController(),
));
}, loading: () {
return Expanded(child: Center(child: UIUtils.loader));
}, error: (error, stackTrace) {
return Text('$error');
});
}
}
Loading

0 comments on commit 3371c12

Please sign in to comment.