improvements

This commit is contained in:
Myzel394 2022-08-14 21:59:29 +02:00
parent 8409939825
commit 13ea760cb8
4 changed files with 161 additions and 75 deletions

68
lib/models/timeline.dart Normal file
View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:share_location/foreign_types/memory.dart';
import 'package:share_location/models/memory_pack.dart';
class TimelineModel extends ChangeNotifier {
final Map<String, MemoryPack> _timeline;
TimelineModel({
Map<String, MemoryPack>? timeline,
}) : _timeline = timeline ?? {};
Map<String, MemoryPack> get values => _timeline;
static TimelineModel fromMemoriesList(
final List<Memory> memories,
) {
final map = <String, List<Memory>>{};
for (final memory in memories) {
final date = DateFormat('yyyy-MM-dd').format(memory.creationDate);
if (map.containsKey(date)) {
map[date]!.add(memory);
} else {
map[date] = [memory];
}
}
final data = Map.fromEntries(
map.entries.map(
(entry) => MapEntry<String, MemoryPack>(
entry.key,
MemoryPack(entry.value),
),
),
);
return TimelineModel(
timeline: data,
);
}
@override
void dispose() {
_timeline.values.forEach((memoryPack) {
memoryPack.dispose();
});
super.dispose();
}
void removeEmptyDates() {
final previousLength = _timeline.length;
_timeline.removeWhere((key, value) => value.memories.isEmpty);
final newLength = _timeline.length;
if (previousLength != newLength) {
notifyListeners();
}
}
DateTime dateAtIndex(final int index) =>
DateTime.parse(_timeline.keys.elementAt(index));
MemoryPack atIndex(final int index) => _timeline.values.elementAt(index);
}

View File

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:share_location/controllers/status_controller.dart'; import 'package:share_location/controllers/status_controller.dart';
import 'package:share_location/enums.dart'; import 'package:share_location/enums.dart';
import 'package:share_location/foreign_types/memory.dart'; import 'package:share_location/foreign_types/memory.dart';
import 'package:share_location/models/memory_pack.dart';
import 'package:share_location/models/timeline_overlay.dart'; import 'package:share_location/models/timeline_overlay.dart';
import 'package:share_location/widgets/status.dart'; import 'package:share_location/widgets/status.dart';
@ -59,6 +60,7 @@ class _MemorySlideState extends State<MemorySlide>
void initializeAnimation(final Duration duration) { void initializeAnimation(final Duration duration) {
final timelineOverlay = context.read<TimelineOverlay>(); final timelineOverlay = context.read<TimelineOverlay>();
final memoryPack = context.read<MemoryPack>();
this.duration = duration; this.duration = duration;
@ -72,7 +74,8 @@ class _MemorySlideState extends State<MemorySlide>
} }
if (controller!.done) { if (controller!.done) {
timelineOverlay.setState(TimelineState.completed); timelineOverlay.reset();
memoryPack.next();
} }
}, ['done']); }, ['done']);
@ -81,10 +84,10 @@ class _MemorySlideState extends State<MemorySlide>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<TimelineOverlay>( return Consumer<MemoryPack>(
builder: (context, overlayController, _) => Status( builder: (context, memoryPack, _) => Status(
controller: controller, controller: controller,
disabled: !overlayController.showOverlay, disabled: memoryPack.paused,
child: MemoryView( child: MemoryView(
creationDate: widget.memory.creationDate, creationDate: widget.memory.creationDate,
location: widget.memory.location, location: widget.memory.location,
@ -99,17 +102,17 @@ class _MemorySlideState extends State<MemorySlide>
if (mounted) { if (mounted) {
initializeAnimation(controller.value.duration); initializeAnimation(controller.value.duration);
overlayController.addListener(() { memoryPack.addListener(() {
if (!mounted) { if (!mounted) {
return; return;
} }
if (overlayController.state == TimelineState.playing) { if (memoryPack.paused) {
controller.play();
} else {
controller.pause(); controller.pause();
} else {
controller.play();
} }
}, ['state']); }, ['paused']);
} }
}, },
), ),

View File

@ -13,12 +13,14 @@ class TimelinePage extends StatefulWidget {
final DateTime date; final DateTime date;
final VoidCallback onPreviousTimeline; final VoidCallback onPreviousTimeline;
final VoidCallback onNextTimeline; final VoidCallback onNextTimeline;
final VoidCallback onMemoryRemoved;
const TimelinePage({ const TimelinePage({
Key? key, Key? key,
required this.date, required this.date,
required this.onPreviousTimeline, required this.onPreviousTimeline,
required this.onNextTimeline, required this.onNextTimeline,
required this.onMemoryRemoved,
}) : super(key: key); }) : super(key: key);
@override @override
@ -48,6 +50,18 @@ class _TimelinePageState extends State<TimelinePage> {
} }
}, ['state']); }, ['state']);
memoryPack.addListener(() {
if (!mounted) {
return;
}
if (memoryPack.paused) {
timelineOverlayController.hideOverlay();
} else {
timelineOverlayController.restoreOverlay();
}
}, ['state']);
memoryPack.addListener(() { memoryPack.addListener(() {
if (!mounted) { if (!mounted) {
return; return;
@ -81,7 +95,6 @@ class _TimelinePageState extends State<TimelinePage> {
final memoryPack = context.read<MemoryPack>(); final memoryPack = context.read<MemoryPack>();
memoryPack.pause(); memoryPack.pause();
timelineOverlayController.hideOverlay();
await showModalBottomSheet( await showModalBottomSheet(
context: context, context: context,
@ -98,16 +111,15 @@ class _TimelinePageState extends State<TimelinePage> {
memoryPack.removeCurrentMemory(); memoryPack.removeCurrentMemory();
memoryPack.resume(); memoryPack.resume();
timelineOverlayController.restoreOverlay();
widget.onMemoryRemoved();
}, },
onTapDown: (_) { onTapDown: (_) {
final memoryPack = context.read<MemoryPack>(); final memoryPack = context.read<MemoryPack>();
memoryPack.pause();
overlayRemover = Timer( overlayRemover = Timer(
const Duration(milliseconds: 200), const Duration(milliseconds: 200),
timelineOverlayController.hideOverlay, memoryPack.pause,
); );
}, },
onTapUp: (_) { onTapUp: (_) {
@ -115,14 +127,11 @@ class _TimelinePageState extends State<TimelinePage> {
overlayRemover?.cancel(); overlayRemover?.cancel();
memoryPack.resume(); memoryPack.resume();
timelineOverlayController.restoreOverlay();
}, },
onTapCancel: () { onTapCancel: () {
final memoryPack = context.read<MemoryPack>(); final memoryPack = context.read<MemoryPack>();
overlayRemover?.cancel(); overlayRemover?.cancel();
timelineOverlayController.restoreOverlay();
memoryPack.resume(); memoryPack.resume();
}, },
onHorizontalDragEnd: (details) { onHorizontalDragEnd: (details) {
@ -146,34 +155,30 @@ class _TimelinePageState extends State<TimelinePage> {
}, },
child: ChangeNotifierProvider<TimelineOverlay>( child: ChangeNotifierProvider<TimelineOverlay>(
create: (_) => timelineOverlayController, create: (_) => timelineOverlayController,
child: Stack( child: Consumer<MemoryPack>(
fit: StackFit.expand, builder: (_, memoryPack, __) => Stack(
children: <Widget>[ fit: StackFit.expand,
Consumer<MemoryPack>( children: <Widget>[
builder: (_, memoryPack, __) { PageView.builder(
return PageView.builder( controller: pageController,
controller: pageController, physics: const NeverScrollableScrollPhysics(),
physics: const NeverScrollableScrollPhysics(), scrollDirection: Axis.horizontal,
scrollDirection: Axis.horizontal, itemBuilder: (_, index) => MemorySlide(
itemBuilder: (_, index) => MemorySlide( key: Key(memoryPack.memories[index].filename),
key: Key(memoryPack.memories[index].filename), memory: memoryPack.memories[index],
memory: memoryPack.memories[index], ),
), itemCount: memoryPack.memories.length,
itemCount: memoryPack.memories.length,
);
},
),
Padding(
padding: const EdgeInsets.only(
top: LARGE_SPACE,
left: MEDIUM_SPACE,
right: MEDIUM_SPACE,
), ),
child: Consumer<TimelineOverlay>( Padding(
builder: (context, overlayController, _) => AnimatedOpacity( padding: const EdgeInsets.only(
top: LARGE_SPACE,
left: MEDIUM_SPACE,
right: MEDIUM_SPACE,
),
child: AnimatedOpacity(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
curve: Curves.linearToEaseOut, curve: Curves.linearToEaseOut,
opacity: overlayController.showOverlay ? 1.0 : 0.0, opacity: memoryPack.paused ? 0.0 : 1.0,
child: Text( child: Text(
DateFormat('dd. MMMM yyyy').format(widget.date), DateFormat('dd. MMMM yyyy').format(widget.date),
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -181,8 +186,26 @@ class _TimelinePageState extends State<TimelinePage> {
), ),
), ),
), ),
), Positioned(
], right: SMALL_SPACE,
bottom: SMALL_SPACE * 2,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE),
child: AnimatedOpacity(
duration: const Duration(milliseconds: 500),
curve: Curves.linearToEaseOut,
opacity: memoryPack.paused ? 0.0 : 1.0,
child: Consumer<MemoryPack>(
builder: (_, memoryPack, __) => Text(
'${memoryPack.currentMemoryIndex + 1}/${memoryPack.memories.length}',
style: Theme.of(context).textTheme.titleSmall,
),
),
),
),
),
],
),
), ),
), ),
); );

View File

@ -1,8 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:share_location/foreign_types/memory.dart'; import 'package:share_location/foreign_types/memory.dart';
import 'package:share_location/models/memory_pack.dart'; import 'package:share_location/models/timeline.dart';
import 'package:share_location/utils/loadable.dart'; import 'package:share_location/utils/loadable.dart';
import 'package:share_location/widgets/timeline_page.dart'; import 'package:share_location/widgets/timeline_page.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
@ -20,7 +19,7 @@ class TimelineScroll extends StatefulWidget {
class _TimelineScrollState extends State<TimelineScroll> with Loadable { class _TimelineScrollState extends State<TimelineScroll> with Loadable {
final pageController = PageController(); final pageController = PageController();
Map<String, MemoryPack>? timeline; TimelineModel? timeline;
@override @override
initState() { initState() {
@ -28,7 +27,18 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
loadTimeline(); loadTimeline();
} }
@override
dispose() {
pageController.dispose();
timeline?.dispose();
super.dispose();
}
Future<void> loadTimeline() async { Future<void> loadTimeline() async {
timeline?.dispose();
final response = await supabase final response = await supabase
.from('memories') .from('memories')
.select() .select()
@ -36,35 +46,16 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
.execute(); .execute();
final memories = List<Memory>.from( final memories = List<Memory>.from(
List<Map<String, dynamic>>.from(response.data).map(Memory.parse)); List<Map<String, dynamic>>.from(response.data).map(Memory.parse));
final timelineMapped = convertMemoriesToTimeline(memories);
setState(() { setState(() {
timeline = timelineMapped; timeline = TimelineModel.fromMemoriesList(memories);
}); });
}
static Map<String, MemoryPack> convertMemoriesToTimeline( timeline!.addListener(() {
final List<Memory> memories, if (mounted) {
) { setState(() {});
final map = <String, List<Memory>>{};
for (final memory in memories) {
final date = DateFormat('yyyy-MM-dd').format(memory.creationDate);
if (map.containsKey(date)) {
map[date]!.add(memory);
} else {
map[date] = [memory];
} }
} });
return Map.fromEntries(
map.entries.map(
(entry) => MapEntry<String, MemoryPack>(
entry.key,
MemoryPack(entry.value),
),
),
);
} }
@override @override
@ -79,11 +70,12 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
body: PageView.builder( body: PageView.builder(
controller: pageController, controller: pageController,
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
itemCount: timeline!.length, itemCount: timeline!.values.length,
itemBuilder: (_, index) => ChangeNotifierProvider<MemoryPack>( itemBuilder: (_, index) => ChangeNotifierProvider.value(
create: (_) => timeline!.values.elementAt(index), value: timeline!.atIndex(index),
child: TimelinePage( child: TimelinePage(
date: DateTime.parse(timeline!.keys.toList()[index]), date: timeline!.dateAtIndex(index),
onMemoryRemoved: () => timeline!.removeEmptyDates(),
onNextTimeline: () { onNextTimeline: () {
pageController.nextPage( pageController.nextPage(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),