improvements

This commit is contained in:
Myzel394 2022-08-16 16:53:42 +02:00
parent f300e52993
commit 3441938e11
9 changed files with 339 additions and 233 deletions

View File

@ -2,6 +2,8 @@ import 'dart:collection';
const DURATION_INFINITY = Duration(days: 999); const DURATION_INFINITY = Duration(days: 999);
const SECONDARY_BUTTONS_DURATION_MULTIPLIER = 1.8; const SECONDARY_BUTTONS_DURATION_MULTIPLIER = 1.8;
final CALENDAR_DATE_IN_DURATION_MULTIPLIER = 1.1;
const PHOTO_SHOW_AFTER_CREATION_DURATION = Duration(milliseconds: 500); const PHOTO_SHOW_AFTER_CREATION_DURATION = Duration(milliseconds: 500);
final UnmodifiableSetView<double> DEFAULT_ZOOM_LEVELS = final UnmodifiableSetView<double> DEFAULT_ZOOM_LEVELS =
UnmodifiableSetView({0.6, 1, 2, 5, 10, 20, 50, 100}); UnmodifiableSetView({0.6, 1, 2, 5, 10, 20, 50, 100});
const CALENDAR_DATE_IN_MAX_DELAY = Duration(milliseconds: 500);

View File

@ -2,6 +2,7 @@ import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:share_location/constants/apis.dart'; import 'package:share_location/constants/apis.dart';
import 'package:share_location/screens/calendar_screen.dart'; import 'package:share_location/screens/calendar_screen.dart';
import 'package:share_location/screens/grant_permission_screen.dart'; import 'package:share_location/screens/grant_permission_screen.dart';
@ -9,10 +10,12 @@ import 'package:share_location/screens/login_screen.dart';
import 'package:share_location/screens/main_screen.dart'; import 'package:share_location/screens/main_screen.dart';
import 'package:share_location/screens/timeline_screen.dart'; import 'package:share_location/screens/timeline_screen.dart';
import 'package:share_location/screens/welcome_screen.dart'; import 'package:share_location/screens/welcome_screen.dart';
import 'package:share_location/utils/auth_required.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
import 'managers/global_values_manager.dart'; import 'managers/global_values_manager.dart';
import 'managers/startup_page_manager.dart'; import 'managers/startup_page_manager.dart';
import 'models/memories.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -35,7 +38,7 @@ void main() async {
runApp(MyApp(initialPage: initialPage)); runApp(MyApp(initialPage: initialPage));
} }
class MyApp extends StatelessWidget { class MyApp extends StatefulWidget {
final String initialPage; final String initialPage;
const MyApp({ const MyApp({
@ -43,35 +46,65 @@ class MyApp extends StatelessWidget {
required this.initialPage, required this.initialPage,
}) : super(key: key); }) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends AuthRequiredState<MyApp> {
final memories = Memories();
@override
void initState() {
super.initState();
memories.addListener(() {
setState(() {
});
}, ['isInitializing']);
}
@override
void onAuthenticated(Session session) {
memories.initialize();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( if (memories.isInitializing) {
title: 'Flutter Demo', return SizedBox();
theme: ThemeData.dark().copyWith( }
textTheme: ThemeData.dark().textTheme.copyWith(
headline1: const TextStyle( return ChangeNotifierProvider.value(
fontSize: 32, value: memories,
fontWeight: FontWeight.w500, child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark().copyWith(
textTheme: ThemeData.dark().textTheme.copyWith(
headline1: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.w500,
),
), ),
inputDecorationTheme: InputDecorationTheme(
filled: true,
helperMaxLines: 10,
errorMaxLines: 10,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
), ),
inputDecorationTheme: InputDecorationTheme(
filled: true,
helperMaxLines: 10,
errorMaxLines: 10,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
), ),
), ),
routes: {
WelcomeScreen.ID: (context) => const WelcomeScreen(),
MainScreen.ID: (context) => const MainScreen(),
LoginScreen.ID: (context) => const LoginScreen(),
TimelineScreen.ID: (context) => const TimelineScreen(),
GrantPermissionScreen.ID: (context) => const GrantPermissionScreen(),
CalendarScreen.ID: (context) => const CalendarScreen(),
},
initialRoute: widget.initialPage,
), ),
routes: {
WelcomeScreen.ID: (context) => const WelcomeScreen(),
MainScreen.ID: (context) => const MainScreen(),
LoginScreen.ID: (context) => const LoginScreen(),
TimelineScreen.ID: (context) => const TimelineScreen(),
GrantPermissionScreen.ID: (context) => const GrantPermissionScreen(),
CalendarScreen.ID: (context) => const CalendarScreen(),
},
initialRoute: initialPage,
); );
} }
} }

View File

@ -0,0 +1,75 @@
import 'package:share_location/foreign_types/memory.dart';
import 'package:share_location/helpers/iterate_months.dart';
class CalendarManager {
final Map<DateTime, Set<String>> _values;
CalendarManager({
required final List<Memory> memories,
}) : _values = mapFromMemoriesList(memories);
static DateTime createDateKey(final DateTime date) =>
DateTime(date.year, date.month, date.day);
static Map<DateTime, Set<String>> mapFromMemoriesList(
final List<Memory> memories) {
final map = <DateTime, Set<String>>{};
for (final memory in memories) {
final key = createDateKey(memory.creationDate);
if (map.containsKey(key)) {
map[key]!.add(memory.id);
} else {
map[key] = {
memory.id,
};
}
}
return map;
}
static Map<DateTime, Map<DateTime, int>> fillEmptyMonths(
Map<DateTime, Map<DateTime, int>> monthMapping) {
final earliestDate =
monthMapping.keys.reduce((a, b) => a.isBefore(b) ? a : b);
final latestDate = monthMapping.keys.reduce((a, b) => a.isAfter(b) ? a : b);
final filledMonthMapping = <DateTime, Map<DateTime, int>>{};
for (final date in iterateMonths(earliestDate, latestDate)) {
filledMonthMapping[date] = monthMapping[date] ?? {};
}
return filledMonthMapping;
}
Map<DateTime, Map<DateTime, int>> getMonthDayAmountMapping() {
final map = <DateTime, Map<DateTime, int>>{};
for (final entry in _values.entries) {
final date = entry.key;
final monthDate = DateTime(date.year, date.month, 1);
final memoryIDs = entry.value;
if (map.containsKey(monthDate)) {
map[monthDate]![date] = memoryIDs.length;
} else {
map[monthDate] = {
date: memoryIDs.length,
};
}
}
return map;
}
Map<DateTime, Map<DateTime, int>> getMappingForList() {
final monthMapping = fillEmptyMonths(getMonthDayAmountMapping());
return Map.fromEntries(
monthMapping.entries.toList()..sort((a, b) => b.key.compareTo(a.key)),
);
}
}

114
lib/models/memories.dart Normal file
View File

@ -0,0 +1,114 @@
import 'package:property_change_notifier/property_change_notifier.dart';
import 'package:share_location/foreign_types/memory.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
final supabase = Supabase.instance.client;
class Memories extends PropertyChangeNotifier<String> {
final List<Memory> _memories = [];
Memories();
RealtimeSubscription? _serverSubscription;
bool _isInitializing = true;
List<Memory> get memories => _memories;
bool get isInitializing => _isInitializing;
@override
void dispose() {
_serverSubscription?.unsubscribe();
super.dispose();
}
void addMemory(final Memory memory) {
_memories.add(memory);
notifyListeners('memories');
}
void addAllMemories(final List<Memory> memories) {
_memories.addAll(memories);
notifyListeners('memories');
}
void removeMemory(final Memory memory) {
_memories.remove(memory);
notifyListeners('memories');
}
void removeMemoryByID(final String id) {
_memories.removeWhere((memory) => memory.id == id);
notifyListeners('memories');
}
void setIsInitializing(final bool value) {
_isInitializing = value;
notifyListeners('isInitializing');
}
void sortMemories() {
_memories.sort((a, b) => b.creationDate.compareTo(a.creationDate));
notifyListeners('memories');
}
Future<void> initialize() async {
setIsInitializing(true);
await _loadInitialData();
setIsInitializing(false);
notifyListeners();
// Watch new updates
_serverSubscription = supabase
.from('memories')
.on(SupabaseEventTypes.all, _onServerUpdate)
.subscribe();
}
Future<void> _onServerUpdate(
final SupabaseRealtimePayload response,
) async {
if (response == null) {
return;
}
switch (response.eventType) {
case 'INSERT':
final memory = Memory.parse(response.newRecord!);
addMemory(memory);
break;
case 'DELETE':
final id = response.oldRecord!['id'];
removeMemoryByID(id);
break;
// Used for easier debugging
case 'UPDATE':
final memory = Memory.parse(response.newRecord!);
final id = response.oldRecord!['id'];
removeMemoryByID(id);
addMemory(memory);
break;
}
sortMemories();
}
Future<void> _loadInitialData() async {
final response = await supabase
.from('memories')
.select()
.order('created_at', ascending: false)
.execute();
final newMemories = List<Memory>.from(
List<Map<String, dynamic>>.from(response.data).map(Memory.parse),
);
addAllMemories(newMemories);
}
}

View File

@ -1,19 +1,17 @@
import 'dart:math'; import 'dart:math';
import 'package:intl/intl.dart';
import 'package:property_change_notifier/property_change_notifier.dart'; import 'package:property_change_notifier/property_change_notifier.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:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
final supabase = Supabase.instance.client; final supabase = Supabase.instance.client;
class TimelineModel extends PropertyChangeNotifier<String> { class TimelineModel extends PropertyChangeNotifier<String> {
final Map<String, MemoryPack> _timeline; final Map<DateTime, List<Memory>> _timeline;
TimelineModel({ TimelineModel({
Map<String, MemoryPack>? timeline, required final List<Memory> memories,
}) : _timeline = timeline ?? {}; }) : _timeline = mapFromMemoriesList(memories);
RealtimeSubscription? _serverSubscription; RealtimeSubscription? _serverSubscription;
@ -22,38 +20,35 @@ class TimelineModel extends PropertyChangeNotifier<String> {
bool _paused = false; bool _paused = false;
bool _isInitializing = true; bool _isInitializing = true;
Map<String, MemoryPack> get values => _timeline; Map<DateTime, List<Memory>> get values => _timeline;
int get length => _timeline.length; int get length => _timeline.length;
int get currentIndex => _currentIndex; int get currentIndex => _currentIndex;
int get memoryIndex => _memoryIndex; int get memoryIndex => _memoryIndex;
bool get paused => _paused; bool get paused => _paused;
bool get isInitializing => _isInitializing; bool get isInitializing => _isInitializing;
DateTime dateAtIndex(final int index) => DateTime dateAtIndex(final int index) => _timeline.keys.elementAt(index);
DateTime.parse(_timeline.keys.elementAt(index));
MemoryPack atIndex(final int index) => _timeline.values.elementAt(index); List<Memory> atIndex(final int index) => _timeline.values.elementAt(index);
MemoryPack get _currentMemoryPack => atIndex(currentIndex); List<Memory> get _currentMemoryPack => atIndex(currentIndex);
bool get _isAtLastMemory => bool get _isAtLastMemory => _memoryIndex == _currentMemoryPack.length - 1;
_memoryIndex == _currentMemoryPack.memories.length - 1; Memory get currentMemory => _currentMemoryPack.elementAt(_memoryIndex);
Memory get currentMemory =>
_currentMemoryPack.memories.elementAt(_memoryIndex);
void _removeEmptyDates() { void _removeEmptyDates() {
_timeline.removeWhere((key, value) => value.memories.isEmpty); _timeline.removeWhere((key, memories) => memories.isEmpty);
} }
static formatCreationDateKey(final DateTime date) => static DateTime createDateKey(final DateTime date) =>
DateFormat('yyyy-MM-dd').format(date); DateTime(date.year, date.month, date.day);
static Map<String, MemoryPack> mapFromMemoriesList( static Map<DateTime, List<Memory>> mapFromMemoriesList(
final List<Memory> memories, final List<Memory> memories,
) { ) {
final map = <String, List<Memory>>{}; final map = <DateTime, List<Memory>>{};
for (final memory in memories) { for (final memory in memories) {
final key = formatCreationDateKey(memory.creationDate); final key = createDateKey(memory.creationDate);
if (map.containsKey(key)) { if (map.containsKey(key)) {
map[key]!.add(memory); map[key]!.add(memory);
@ -62,14 +57,7 @@ class TimelineModel extends PropertyChangeNotifier<String> {
} }
} }
return Map.fromEntries( return map;
map.entries.map(
(entry) => MapEntry<String, MemoryPack>(
entry.key,
MemoryPack(entry.value),
),
),
);
} }
@override @override
@ -85,7 +73,7 @@ class TimelineModel extends PropertyChangeNotifier<String> {
void setMemoryIndex(final int index) { void setMemoryIndex(final int index) {
_memoryIndex = min( _memoryIndex = min(
_timeline.values.elementAt(_currentIndex).memories.length - 1, _timeline.values.elementAt(_currentIndex).length - 1,
max(0, index), max(0, index),
); );
notifyListeners('memoryIndex'); notifyListeners('memoryIndex');
@ -118,7 +106,7 @@ class TimelineModel extends PropertyChangeNotifier<String> {
} }
setCurrentIndex(currentIndex - 1); setCurrentIndex(currentIndex - 1);
setMemoryIndex(_currentMemoryPack.memories.length - 1); setMemoryIndex(_currentMemoryPack.length - 1);
} }
void nextMemory() { void nextMemory() {
@ -137,100 +125,13 @@ class TimelineModel extends PropertyChangeNotifier<String> {
} }
} }
Future<void> initialize() async { void refresh(final List<Memory> memories) {
setIsInitializing(true); setIsInitializing(true);
await _listenToServer(); _timeline.clear();
_timeline.addAll(mapFromMemoriesList(memories));
_removeEmptyDates();
setIsInitializing(false); setIsInitializing(false);
} }
void _insertMemory(final Memory memory) {
final key = formatCreationDateKey(memory.creationDate);
if (!_timeline.containsKey(key)) {
_timeline[key] = MemoryPack([memory]);
return;
}
final memoryPack = _timeline[key]!;
memoryPack.addMemory(memory);
}
void _updateMemory(final String id, final Memory memory) {
final key = formatCreationDateKey(memory.creationDate);
if (!_timeline.containsKey(key)) {
_timeline[key] = MemoryPack([memory]);
return;
}
final memoryPack = _timeline[key]!;
memoryPack.updateWithNewMemory(id, memory);
}
void _deleteMemory(final String id) {
// Search for correct `Memory` and remove it
for (final memories in _timeline.values) {
memories.memories.removeWhere((memory) => memory.id == id);
}
_removeEmptyDates();
}
Future<void> _fetchInitialData() async {
final response = await supabase
.from('memories')
.select()
.order('created_at', ascending: false)
.execute();
final memories = List<Memory>.from(
List<Map<String, dynamic>>.from(response.data).map(Memory.parse),
);
values
..clear()
..addAll(mapFromMemoriesList(memories));
}
Future<void> _onServerUpdate(
final SupabaseRealtimePayload response,
) async {
if (response == null) {
return;
}
switch (response.eventType) {
case 'INSERT':
final memory = Memory.parse(response.newRecord!);
_insertMemory(memory);
break;
case 'UPDATE':
final memoryID = response.oldRecord!['id'];
final memory = Memory.parse(response.newRecord!);
_updateMemory(memoryID, memory);
break;
case 'DELETE':
final id = response.oldRecord!['id'];
_deleteMemory(id);
break;
}
notifyListeners();
}
Future<void> _listenToServer() async {
await _fetchInitialData();
notifyListeners();
// Watch new updates
_serverSubscription = supabase
.from('memories')
.on(SupabaseEventTypes.all, _onServerUpdate)
.subscribe();
}
} }

View File

@ -1,87 +1,59 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:provider/provider.dart';
import 'package:share_location/constants/spacing.dart'; import 'package:share_location/constants/spacing.dart';
import 'package:share_location/helpers/iterate_months.dart'; import 'package:share_location/managers/calendar_manager.dart';
import 'package:share_location/models/calendar.dart'; import 'package:share_location/models/memories.dart';
import 'package:share_location/widgets/calendar_month.dart'; import 'package:share_location/widgets/calendar_month.dart';
import 'package:share_location/widgets/days_of_week_strip.dart'; import 'package:share_location/widgets/days_of_week_strip.dart';
class CalendarScreen extends StatefulWidget { class CalendarScreen extends StatelessWidget {
static const ID = 'calendar'; static const ID = 'calendar';
const CalendarScreen({Key? key}) : super(key: key); const CalendarScreen({
Key? key,
@override }) : super(key: key);
State<CalendarScreen> createState() => _CalendarScreenState();
}
class _CalendarScreenState extends State<CalendarScreen> {
final calendar = CalendarModel();
@override
void initState() {
super.initState();
calendar.initialize();
calendar.addListener(() {
setState(() {});
});
}
static Map<DateTime, Map<DateTime, int>> fillEmptyMonths(
Map<DateTime, Map<DateTime, int>> monthMapping) {
final earliestDate =
monthMapping.keys.reduce((a, b) => a.isBefore(b) ? a : b);
final latestDate = monthMapping.keys.reduce((a, b) => a.isAfter(b) ? a : b);
final filledMonthMapping = <DateTime, Map<DateTime, int>>{};
for (final date in iterateMonths(earliestDate, latestDate)) {
filledMonthMapping[date] = monthMapping[date] ?? {};
}
return filledMonthMapping;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (calendar.isInitializing) { final memoriesManager = context.read<Memories>();
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
final theme = Theme.of(context); final theme = Theme.of(context);
final monthMapping = fillEmptyMonths(calendar.getMonthDayAmountMapping());
final sortedMonthMapping = Map.fromEntries(
monthMapping.entries.toList()..sort((a, b) => b.key.compareTo(a.key)),
);
return Scaffold( final calendarManager = CalendarManager(memories: memoriesManager.memories);
body: CustomScrollView( final monthMapping = calendarManager.getMappingForList();
reverse: true,
slivers: [ return Consumer<Memories>(
SliverStickyHeader( builder: (context, memories, _) => Scaffold(
header: Container( body: Padding(
color: theme.canvasColor, padding: const EdgeInsets.symmetric(vertical: MEDIUM_SPACE),
padding: const EdgeInsets.symmetric(vertical: SMALL_SPACE), child: CustomScrollView(
child: const DaysOfWeekStrip(), reverse: true,
), slivers: [
sliver: SliverList( SliverStickyHeader(
delegate: SliverChildBuilderDelegate( header: Container(
(context, index) => CalendarMonth( color: theme.canvasColor,
year: sortedMonthMapping.keys.elementAt(index).year, padding: const EdgeInsets.symmetric(vertical: SMALL_SPACE),
month: sortedMonthMapping.keys.elementAt(index).month, child: const DaysOfWeekStrip(),
dayAmountMap: sortedMonthMapping.values.elementAt(index), ),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final date = monthMapping.keys.elementAt(index);
final dayMapping = monthMapping.values.elementAt(index);
return CalendarMonth(
year: date.year,
month: date.month,
dayAmountMap: dayMapping,
);
},
childCount: monthMapping.length,
),
), ),
childCount: sortedMonthMapping.length,
), ),
), ],
), ),
], ),
), ),
); );
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:share_location/extensions/date.dart'; import 'package:share_location/extensions/date.dart';
import 'package:share_location/models/memories.dart';
import 'package:share_location/models/timeline.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';
@ -26,7 +27,7 @@ class TimelineScreen extends StatefulWidget {
class _TimelineScreenState extends State<TimelineScreen> with Loadable { class _TimelineScreenState extends State<TimelineScreen> with Loadable {
final pageController = PageController(); final pageController = PageController();
final timeline = TimelineModel(); late final TimelineModel timeline;
bool _ignorePageChanges = false; bool _ignorePageChanges = false;
Future<void> _goToPage(final int page) async { Future<void> _goToPage(final int page) async {
@ -45,7 +46,13 @@ class _TimelineScreenState extends State<TimelineScreen> with Loadable {
initState() { initState() {
super.initState(); super.initState();
timeline.initialize(); final memoriesModel = context.read<Memories>();
timeline = TimelineModel(memories: memoriesModel.memories);
memoriesModel.addListener(() {
timeline.refresh(memoriesModel.memories);
}, ['memories']);
// Update page view // Update page view
timeline.addListener(() async { timeline.addListener(() async {
@ -86,22 +93,16 @@ class _TimelineScreenState extends State<TimelineScreen> with Loadable {
return timeline.values.keys return timeline.values.keys
.toList() .toList()
.indexWhere((date) => DateTime.parse(date).isSameDay(widget.date!)); .indexWhere((date) => date.isSameDay(widget.date!));
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (timeline.isInitializing) {
return const Center(
child: CircularProgressIndicator(),
);
}
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
await Navigator.pushNamed(context, CalendarScreen.ID); await Navigator.pushReplacementNamed(context, CalendarScreen.ID);
return true; return false;
}, },
child: Scaffold( child: Scaffold(
body: ChangeNotifierProvider.value( body: ChangeNotifierProvider.value(
@ -124,7 +125,7 @@ class _TimelineScreenState extends State<TimelineScreen> with Loadable {
}, },
itemBuilder: (_, index) => TimelinePage( itemBuilder: (_, index) => TimelinePage(
date: timeline.dateAtIndex(index), date: timeline.dateAtIndex(index),
memoryPack: timeline.atIndex(index), memories: timeline.atIndex(index),
), ),
), ),
), ),

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_calendar_widget/flutter_calendar_widget.dart'; import 'package:flutter_calendar_widget/flutter_calendar_widget.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:share_location/constants/spacing.dart'; import 'package:share_location/constants/spacing.dart';
import 'package:share_location/constants/values.dart';
import 'package:share_location/screens/timeline_screen.dart'; import 'package:share_location/screens/timeline_screen.dart';
import 'package:share_location/widgets/delay_render.dart'; import 'package:share_location/widgets/delay_render.dart';
import 'package:share_location/widgets/fade_and_move_in_animation.dart'; import 'package:share_location/widgets/fade_and_move_in_animation.dart';
@ -37,11 +38,18 @@ class MonthCalendarBuilder extends CalendarBuilder {
return amount / highestAmountOfEvents; return amount / highestAmountOfEvents;
}(); }();
final duration = Duration(milliseconds: Random().nextInt(800)); final delay = Duration(
microseconds:
Random().nextInt(CALENDAR_DATE_IN_MAX_DELAY.inMicroseconds));
return DelayRender( return DelayRender(
delay: duration, delay: delay,
child: FadeAndMoveInAnimation( child: FadeAndMoveInAnimation(
opacityDuration:
DEFAULT_OPACITY_DURATION * CALENDAR_DATE_IN_DURATION_MULTIPLIER,
translationDuration:
DEFAULT_TRANSLATION_DURATION * CALENDAR_DATE_IN_DURATION_MULTIPLIER,
translationOffset: const Offset(0.0, -MEDIUM_SPACE),
child: Opacity( child: Opacity(
opacity: () { opacity: () {
if (type.isOutSide) { if (type.isOutSide) {

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:share_location/models/memory_pack.dart'; import 'package:share_location/foreign_types/memory.dart';
import 'package:share_location/models/timeline.dart'; import 'package:share_location/models/timeline.dart';
import 'package:share_location/models/timeline_overlay.dart'; import 'package:share_location/models/timeline_overlay.dart';
import 'package:share_location/widgets/memory_sheet.dart'; import 'package:share_location/widgets/memory_sheet.dart';
@ -11,12 +11,12 @@ import 'package:share_location/widgets/timeline_overlay.dart';
class TimelinePage extends StatefulWidget { class TimelinePage extends StatefulWidget {
final DateTime date; final DateTime date;
final MemoryPack memoryPack; final List<Memory> memories;
const TimelinePage({ const TimelinePage({
Key? key, Key? key,
required this.date, required this.date,
required this.memoryPack, required this.memories,
}) : super(key: key); }) : super(key: key);
@override @override
@ -165,14 +165,14 @@ class _TimelinePageState extends State<TimelinePage> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemBuilder: (_, index) => MemorySlide( itemBuilder: (_, index) => MemorySlide(
key: Key(widget.memoryPack.memories[index].filename), key: Key(widget.memories[index].filename),
memory: widget.memoryPack.memories[index], memory: widget.memories[index],
), ),
itemCount: widget.memoryPack.memories.length, itemCount: widget.memories.length,
), ),
TimelineOverlay( TimelineOverlay(
date: widget.date, date: widget.date,
memoriesAmount: widget.memoryPack.memories.length, memoriesAmount: widget.memories.length,
memoryIndex: timeline.memoryIndex + 1, memoryIndex: timeline.memoryIndex + 1,
), ),
], ],