mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-18 23:35:25 +02:00
added save from calendar functionality
This commit is contained in:
parent
4170ec99ea
commit
ff63d87600
@ -3,6 +3,16 @@ extension DateExtensions on DateTime {
|
|||||||
return year == other.year && month == other.month && day == other.day;
|
return year == other.year && month == other.month && day == other.day;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime asNormalizedDate() => DateTime(year, month, day);
|
DateTime asNormalizedDate() => DateTime(
|
||||||
DateTime asNormalizedDateTime() => DateTime(year, month, day, hour, minute);
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
DateTime asNormalizedDateTime() =>
|
||||||
|
DateTime(year, month, day, hour, minute, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:gallery_saver/gallery_saver.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:quid_faciam_hodie/enums.dart';
|
import 'package:quid_faciam_hodie/enums.dart';
|
||||||
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
||||||
@ -44,4 +45,17 @@ class Memory {
|
|||||||
'memories',
|
'memories',
|
||||||
filePath,
|
filePath,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<void> saveFileToGallery() async {
|
||||||
|
final file = await downloadToFile();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MemoryType.photo:
|
||||||
|
await GallerySaver.saveImage(file.path);
|
||||||
|
break;
|
||||||
|
case MemoryType.video:
|
||||||
|
await GallerySaver.saveVideo(file.path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,17 @@
|
|||||||
|
|
||||||
|
|
||||||
"calendarScreenTitle": "Calendar",
|
"calendarScreenTitle": "Calendar",
|
||||||
|
"calendarScreenSelectionTitle": "Selected {memories,plural, =1{one memory} other{{memories} memories}}",
|
||||||
|
"@calendarScreenSelectionTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"days": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"memories": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
"timelineScreenTitle": "Timeline",
|
"timelineScreenTitle": "Timeline",
|
||||||
|
64
lib/models/calendar_model.dart
Normal file
64
lib/models/calendar_model.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:quid_faciam_hodie/extensions/date.dart';
|
||||||
|
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
||||||
|
|
||||||
|
// The `CalendarModel` is only used to give the calendar day's a way to
|
||||||
|
// send whether they have been pressed to the parent widget.
|
||||||
|
class CalendarModel extends ChangeNotifier {
|
||||||
|
bool _isSavingToGallery = false;
|
||||||
|
final Set<DateTime> _selectedDates = {};
|
||||||
|
|
||||||
|
bool get isInSelectMode => _selectedDates.isNotEmpty;
|
||||||
|
bool get isSavingToGallery => _isSavingToGallery;
|
||||||
|
Set<DateTime> get selectedDates => _selectedDates;
|
||||||
|
|
||||||
|
void setIsInSelectMode(final bool value) {
|
||||||
|
if (_isSavingToGallery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
_selectedDates.clear();
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIsSavingToGallery(final bool value) {
|
||||||
|
_isSavingToGallery = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDate(final DateTime date) {
|
||||||
|
_selectedDates.add(date.asNormalizedDate());
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDate(final DateTime date) {
|
||||||
|
_selectedDates.removeWhere((element) => element == date.asNormalizedDate());
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearDates() {
|
||||||
|
_selectedDates.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleDate(final DateTime date) {
|
||||||
|
if (checkWhetherDateIsSelected(date)) {
|
||||||
|
removeDate(date);
|
||||||
|
} else {
|
||||||
|
addDate(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<Memory> filterMemories(final Iterable<Memory> memories) =>
|
||||||
|
memories.where(
|
||||||
|
(memory) => _selectedDates.contains(
|
||||||
|
memory.creationDate.asNormalizedDate(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
bool checkWhetherDateIsSelected(final DateTime date) {
|
||||||
|
return _selectedDates.contains(date.asNormalizedDate());
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
@ -5,7 +6,9 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
import 'package:quid_faciam_hodie/managers/calendar_manager.dart';
|
import 'package:quid_faciam_hodie/managers/calendar_manager.dart';
|
||||||
|
import 'package:quid_faciam_hodie/models/calendar_model.dart';
|
||||||
import 'package:quid_faciam_hodie/models/memories.dart';
|
import 'package:quid_faciam_hodie/models/memories.dart';
|
||||||
|
import 'package:quid_faciam_hodie/screens/calendar_screen/save_to_gallery_modal.dart';
|
||||||
|
|
||||||
import 'calendar_screen/calendar_month.dart';
|
import 'calendar_screen/calendar_month.dart';
|
||||||
import 'calendar_screen/days_of_week_strip.dart';
|
import 'calendar_screen/days_of_week_strip.dart';
|
||||||
@ -27,50 +30,98 @@ class CalendarScreen extends StatelessWidget {
|
|||||||
final monthMapping = calendarManager.getMappingForList();
|
final monthMapping = calendarManager.getMappingForList();
|
||||||
|
|
||||||
return Consumer<Memories>(
|
return Consumer<Memories>(
|
||||||
builder: (context, memories, _) => PlatformScaffold(
|
builder: (context, memories, _) => ChangeNotifierProvider<CalendarModel>(
|
||||||
appBar: isCupertino(context)
|
create: (_) => CalendarModel(),
|
||||||
? PlatformAppBar(
|
child: Consumer<CalendarModel>(
|
||||||
title: Text(localizations.calendarScreenTitle),
|
builder: (_, calendar, __) => PlatformScaffold(
|
||||||
)
|
appBar: (calendar.isInSelectMode || isCupertino(context))
|
||||||
: null,
|
? PlatformAppBar(
|
||||||
body: Padding(
|
leading: calendar.isInSelectMode
|
||||||
padding: EdgeInsets.only(
|
? PlatformIconButton(
|
||||||
top: isCupertino(context) ? HUGE_SPACE : MEDIUM_SPACE,
|
icon: Icon(context.platformIcons.clear),
|
||||||
),
|
onPressed: calendar.clearDates,
|
||||||
child: CustomScrollView(
|
)
|
||||||
reverse: true,
|
: null,
|
||||||
slivers: [
|
trailingActions: calendar.isInSelectMode
|
||||||
SliverStickyHeader(
|
? <Widget>[
|
||||||
header: Container(
|
IconButton(
|
||||||
color: platformThemeData(
|
onPressed: () async {
|
||||||
context,
|
calendar.setIsSavingToGallery(true);
|
||||||
material: (data) => data.canvasColor,
|
|
||||||
cupertino: (data) => data.barBackgroundColor,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: SMALL_SPACE),
|
|
||||||
child: const DaysOfWeekStrip(),
|
|
||||||
),
|
|
||||||
sliver: SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(context, index) {
|
|
||||||
if (index == monthMapping.length) {
|
|
||||||
return MemoriesData();
|
|
||||||
}
|
|
||||||
|
|
||||||
final date = monthMapping.keys.elementAt(index);
|
final memoriesToSave =
|
||||||
final dayMapping = monthMapping.values.elementAt(index);
|
calendar.filterMemories(memories.memories);
|
||||||
|
|
||||||
return CalendarMonth(
|
final hasSavedAll = await showPlatformDialog(
|
||||||
year: date.year,
|
context: context,
|
||||||
month: date.month,
|
builder: (_) => SaveToGalleryModal(
|
||||||
dayAmountMap: dayMapping,
|
memories: memoriesToSave,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
childCount: monthMapping.length + 1,
|
|
||||||
),
|
if (hasSavedAll == true) {
|
||||||
),
|
calendar.clearDates();
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar.setIsSavingToGallery(true);
|
||||||
|
},
|
||||||
|
icon: Icon(isMaterial(context)
|
||||||
|
? Icons.download
|
||||||
|
: CupertinoIcons.down_arrow),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
title: Text(
|
||||||
|
calendar.isInSelectMode
|
||||||
|
? localizations.calendarScreenSelectionTitle(
|
||||||
|
calendar.selectedDates.length,
|
||||||
|
calendar.filterMemories(memories.memories).length,
|
||||||
|
)
|
||||||
|
: localizations.calendarScreenTitle,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
body: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: isCupertino(context) ? HUGE_SPACE : MEDIUM_SPACE,
|
||||||
),
|
),
|
||||||
],
|
child: CustomScrollView(
|
||||||
|
reverse: true,
|
||||||
|
slivers: [
|
||||||
|
SliverStickyHeader(
|
||||||
|
header: Container(
|
||||||
|
color: platformThemeData(
|
||||||
|
context,
|
||||||
|
material: (data) => data.canvasColor,
|
||||||
|
cupertino: (data) => data.barBackgroundColor,
|
||||||
|
),
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: SMALL_SPACE),
|
||||||
|
child: const DaysOfWeekStrip(),
|
||||||
|
),
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
if (index == monthMapping.length) {
|
||||||
|
return const MemoriesData();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 + 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_calendar_widget/flutter_calendar_widget.dart';
|
import 'package:flutter_calendar_widget/flutter_calendar_widget.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/values.dart';
|
import 'package:quid_faciam_hodie/constants/values.dart';
|
||||||
import 'package:quid_faciam_hodie/extensions/date.dart';
|
import 'package:quid_faciam_hodie/extensions/date.dart';
|
||||||
|
import 'package:quid_faciam_hodie/models/calendar_model.dart';
|
||||||
import 'package:quid_faciam_hodie/screens/timeline_screen.dart';
|
import 'package:quid_faciam_hodie/screens/timeline_screen.dart';
|
||||||
import 'package:quid_faciam_hodie/widgets/delay_render.dart';
|
import 'package:quid_faciam_hodie/widgets/delay_render.dart';
|
||||||
import 'package:quid_faciam_hodie/widgets/fade_and_move_in_animation.dart';
|
import 'package:quid_faciam_hodie/widgets/fade_and_move_in_animation.dart';
|
||||||
@ -30,75 +33,95 @@ class MonthCalendarBuilder extends CalendarBuilder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildDate(DateTime dateTime, DateType type, List events) {
|
Widget buildDate(DateTime dateTime, DateType type, List events) {
|
||||||
final backgroundPercentage = () {
|
|
||||||
if (events.isEmpty) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
final highestAmountOfEvents = events[0];
|
|
||||||
final amount = events[1];
|
|
||||||
return amount / highestAmountOfEvents;
|
|
||||||
}();
|
|
||||||
|
|
||||||
final delay = Duration(
|
final delay = Duration(
|
||||||
microseconds: Random().nextInt(CALENDAR_DATE_IN_MAX_DELAY.inMicroseconds),
|
microseconds: Random().nextInt(CALENDAR_DATE_IN_MAX_DELAY.inMicroseconds),
|
||||||
);
|
);
|
||||||
|
|
||||||
return DelayRender(
|
return Consumer<CalendarModel>(
|
||||||
delay: delay,
|
builder: (_, calendar, __) {
|
||||||
child: FadeAndMoveInAnimation(
|
final isSelected = calendar.checkWhetherDateIsSelected(dateTime);
|
||||||
opacityDuration:
|
final backgroundPercentage = () {
|
||||||
DEFAULT_OPACITY_DURATION * CALENDAR_DATE_IN_DURATION_MULTIPLIER,
|
if (isSelected) {
|
||||||
translationDuration:
|
|
||||||
DEFAULT_TRANSLATION_DURATION * CALENDAR_DATE_IN_DURATION_MULTIPLIER,
|
|
||||||
translationOffset: const Offset(0.0, -MEDIUM_SPACE),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: () {
|
|
||||||
if (type.isOutSide) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dateTime.isAfter(DateTime.now())) {
|
|
||||||
return 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}(),
|
}
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(TINY_SPACE),
|
if (events.isEmpty) {
|
||||||
child: ClipRRect(
|
return 0.0;
|
||||||
borderRadius: BorderRadius.circular(SMALL_SPACE),
|
}
|
||||||
child: Stack(
|
|
||||||
alignment: style.dayAlignment,
|
final highestAmountOfEvents = events[0];
|
||||||
children: [
|
final amount = events[1];
|
||||||
SizedBox(
|
return amount / highestAmountOfEvents;
|
||||||
child: Container(
|
}();
|
||||||
color: textStyle.selectedDayTextColor
|
|
||||||
.withOpacity(backgroundPercentage),
|
return DelayRender(
|
||||||
),
|
delay: delay,
|
||||||
),
|
child: FadeAndMoveInAnimation(
|
||||||
Container(
|
opacityDuration:
|
||||||
alignment: Alignment.center,
|
DEFAULT_OPACITY_DURATION * CALENDAR_DATE_IN_DURATION_MULTIPLIER,
|
||||||
child: Text(
|
translationDuration: DEFAULT_TRANSLATION_DURATION *
|
||||||
dateTime.day.toString(),
|
CALENDAR_DATE_IN_DURATION_MULTIPLIER,
|
||||||
style: TextStyle(
|
translationOffset: const Offset(0.0, -MEDIUM_SPACE),
|
||||||
color: backgroundPercentage > .5
|
child: Opacity(
|
||||||
? textStyle.focusedDayTextColor
|
opacity: () {
|
||||||
: textStyle.dayTextColor,
|
if (type.isOutSide) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateTime.isAfter(DateTime.now())) {
|
||||||
|
return 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0;
|
||||||
|
}(),
|
||||||
|
child: GestureDetector(
|
||||||
|
onLongPress: () {
|
||||||
|
if (events.isNotEmpty) {
|
||||||
|
HapticFeedback.heavyImpact();
|
||||||
|
|
||||||
|
calendar.addDate(dateTime);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Material(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(TINY_SPACE),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(SMALL_SPACE),
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: isSelected
|
||||||
|
? textStyle.outsideDayTextColor
|
||||||
|
: textStyle.selectedDayTextColor
|
||||||
|
.withOpacity(backgroundPercentage),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
dateTime.day.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: backgroundPercentage > .5
|
||||||
|
? textStyle.focusedDayTextColor
|
||||||
|
: textStyle.dayTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CalendarMonth extends StatelessWidget {
|
class CalendarMonth extends StatefulWidget {
|
||||||
final Map<DateTime, int> dayAmountMap;
|
final Map<DateTime, int> dayAmountMap;
|
||||||
final int year;
|
final int year;
|
||||||
final int month;
|
final int month;
|
||||||
@ -110,73 +133,98 @@ class CalendarMonth extends StatelessWidget {
|
|||||||
required this.month,
|
required this.month,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
int get highestAmount =>
|
@override
|
||||||
dayAmountMap.values.isEmpty ? 0 : dayAmountMap.values.reduce(max);
|
State<CalendarMonth> createState() => _CalendarMonthState();
|
||||||
DateTime get firstDate => DateTime(year, month, 1);
|
}
|
||||||
DateTime get lastDate =>
|
|
||||||
DateTime(year, month, DateUtils.getDaysInMonth(year, month));
|
class _CalendarMonthState extends State<CalendarMonth> {
|
||||||
|
int get highestAmount => widget.dayAmountMap.values.isEmpty
|
||||||
|
? 0
|
||||||
|
: widget.dayAmountMap.values.reduce(max);
|
||||||
|
|
||||||
|
DateTime get firstDate => DateTime(widget.year, widget.month, 1);
|
||||||
|
|
||||||
|
DateTime get lastDate => DateTime(widget.year, widget.month,
|
||||||
|
DateUtils.getDaysInMonth(widget.year, widget.month));
|
||||||
|
|
||||||
bool doesDateExist(final DateTime date) =>
|
bool doesDateExist(final DateTime date) =>
|
||||||
dayAmountMap.keys.contains(date.asNormalizedDate());
|
widget.dayAmountMap.keys.contains(date.asNormalizedDate());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlutterCalendar(
|
return Consumer<CalendarModel>(
|
||||||
focusedDate: firstDate,
|
builder: (_, calendar, __) => FlutterCalendar(
|
||||||
selectionMode: CalendarSelectionMode.single,
|
focusedDate: firstDate,
|
||||||
calendarBuilder: MonthCalendarBuilder(),
|
selectionMode: CalendarSelectionMode.single,
|
||||||
events: EventList(events: {
|
calendarBuilder: MonthCalendarBuilder(),
|
||||||
DateTime(1990, 1, 1): [
|
events: EventList(events: {
|
||||||
highestAmount,
|
DateTime(1990, 1, 1): [
|
||||||
],
|
highestAmount,
|
||||||
...Map.fromEntries(
|
],
|
||||||
dayAmountMap.entries.map((entry) {
|
...Map.fromEntries(
|
||||||
return MapEntry(
|
widget.dayAmountMap.entries.map((entry) {
|
||||||
entry.key,
|
return MapEntry(
|
||||||
[highestAmount, entry.value],
|
entry.key,
|
||||||
);
|
[highestAmount, entry.value],
|
||||||
}),
|
);
|
||||||
),
|
}),
|
||||||
}),
|
|
||||||
minDate: firstDate,
|
|
||||||
maxDate: lastDate,
|
|
||||||
startingDayOfWeek: DayOfWeek.mon,
|
|
||||||
onDayPressed: (date) {
|
|
||||||
if (!doesDateExist(date)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => TimelineScreen(date: date),
|
|
||||||
),
|
),
|
||||||
);
|
}),
|
||||||
},
|
minDate: firstDate,
|
||||||
style: const CalendarStyle(
|
maxDate: lastDate,
|
||||||
calenderMargin: EdgeInsets.symmetric(vertical: MEDIUM_SPACE),
|
startingDayOfWeek: DayOfWeek.mon,
|
||||||
),
|
onDayPressed: (date) {
|
||||||
textStyle: CalendarTextStyle(
|
if (calendar.isInSelectMode &&
|
||||||
headerTextStyle: platformThemeData(
|
widget.dayAmountMap.keys.contains(date.asNormalizedDate())) {
|
||||||
context,
|
HapticFeedback.selectionClick();
|
||||||
material: (data) => data.textTheme.subtitle1!,
|
|
||||||
cupertino: (data) => data.textTheme.navTitleTextStyle,
|
calendar.toggleDate(date);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doesDateExist(date)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => TimelineScreen(date: date),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
style: const CalendarStyle(
|
||||||
|
calenderMargin: EdgeInsets.symmetric(vertical: MEDIUM_SPACE),
|
||||||
),
|
),
|
||||||
dayTextColor: platformThemeData(
|
textStyle: CalendarTextStyle(
|
||||||
context,
|
headerTextStyle: platformThemeData(
|
||||||
material: (data) => data.textTheme.bodyText1!.color!,
|
context,
|
||||||
cupertino: (data) => data.textTheme.textStyle.color!,
|
material: (data) => data.textTheme.subtitle1!,
|
||||||
),
|
cupertino: (data) => data.textTheme.navTitleTextStyle,
|
||||||
// Background color
|
),
|
||||||
selectedDayTextColor: platformThemeData(
|
dayTextColor: platformThemeData(
|
||||||
context,
|
context,
|
||||||
material: (data) => data.textTheme.bodyText1!.color!,
|
material: (data) => data.textTheme.bodyText1!.color!,
|
||||||
cupertino: (data) => data.textTheme.textStyle.color!,
|
cupertino: (data) => data.textTheme.textStyle.color!,
|
||||||
),
|
),
|
||||||
// Foreground color
|
// Background color
|
||||||
focusedDayTextColor: platformThemeData(
|
selectedDayTextColor: platformThemeData(
|
||||||
context,
|
context,
|
||||||
material: (data) => data.dialogBackgroundColor,
|
material: (data) => data.textTheme.bodyText1!.color!,
|
||||||
cupertino: (data) => data.barBackgroundColor,
|
cupertino: (data) => data.textTheme.textStyle.color!,
|
||||||
|
),
|
||||||
|
// Foreground color
|
||||||
|
focusedDayTextColor: platformThemeData(
|
||||||
|
context,
|
||||||
|
material: (data) => data.dialogBackgroundColor,
|
||||||
|
cupertino: (data) => data.barBackgroundColor,
|
||||||
|
),
|
||||||
|
// Primary theme color
|
||||||
|
outsideDayTextColor: platformThemeData(
|
||||||
|
context,
|
||||||
|
material: (data) => data.colorScheme.primary,
|
||||||
|
cupertino: (data) => data.primaryColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
72
lib/screens/calendar_screen/save_to_gallery_modal.dart
Normal file
72
lib/screens/calendar_screen/save_to_gallery_modal.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
|
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
||||||
|
import 'package:quid_faciam_hodie/utils/theme.dart';
|
||||||
|
import 'package:quid_faciam_hodie/widgets/modal_sheet.dart';
|
||||||
|
|
||||||
|
class SaveToGalleryModal extends StatefulWidget {
|
||||||
|
final Iterable<Memory> memories;
|
||||||
|
|
||||||
|
const SaveToGalleryModal({
|
||||||
|
Key? key,
|
||||||
|
required this.memories,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SaveToGalleryModal> createState() => _SaveToGalleryModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SaveToGalleryModalState extends State<SaveToGalleryModal> {
|
||||||
|
int currentMemory = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
downloadMemories();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> downloadMemories() async {
|
||||||
|
for (final memory in widget.memories) {
|
||||||
|
await memory.saveFileToGallery();
|
||||||
|
|
||||||
|
if (widget.memories.last != memory) {
|
||||||
|
setState(() {
|
||||||
|
currentMemory = currentMemory + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ModalSheet(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
'Saving to gallery',
|
||||||
|
style: getTitleTextStyle(context),
|
||||||
|
),
|
||||||
|
const SizedBox(height: LARGE_SPACE),
|
||||||
|
Text(
|
||||||
|
'${currentMemory}/${widget.memories.length}',
|
||||||
|
style: getBodyTextTextStyle(context),
|
||||||
|
),
|
||||||
|
const SizedBox(height: MEDIUM_SPACE),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: currentMemory / widget.memories.length,
|
||||||
|
),
|
||||||
|
const SizedBox(height: MEDIUM_SPACE),
|
||||||
|
Text(
|
||||||
|
widget.memories.elementAt(currentMemory).annotation,
|
||||||
|
style: getSubTitleTextStyle(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,7 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
import 'package:gallery_saver/gallery_saver.dart';
|
|
||||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
import 'package:quid_faciam_hodie/enums.dart';
|
|
||||||
import 'package:quid_faciam_hodie/extensions/snackbar.dart';
|
import 'package:quid_faciam_hodie/extensions/snackbar.dart';
|
||||||
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
||||||
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
||||||
@ -50,16 +48,7 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
|||||||
final localizations = AppLocalizations.of(context)!;
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final file = await widget.memory.downloadToFile();
|
await widget.memory.saveFileToGallery();
|
||||||
|
|
||||||
switch (widget.memory.type) {
|
|
||||||
case MemoryType.photo:
|
|
||||||
await GallerySaver.saveImage(file.path);
|
|
||||||
break;
|
|
||||||
case MemoryType.video:
|
|
||||||
await GallerySaver.saveVideo(file.path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
|
@ -14,38 +14,38 @@ class ModalSheet extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SingleChildScrollView(
|
return Align(
|
||||||
padding: const EdgeInsets.only(top: MEDIUM_SPACE),
|
alignment: Alignment.bottomCenter,
|
||||||
child: Column(
|
child: PlatformWidget(
|
||||||
mainAxisSize: MainAxisSize.min,
|
material: (_, __) => Container(
|
||||||
children: [
|
decoration: BoxDecoration(
|
||||||
PlatformWidget(
|
borderRadius: const BorderRadius.only(
|
||||||
material: (_, __) => Container(
|
topLeft: Radius.circular(LARGE_SPACE),
|
||||||
decoration: BoxDecoration(
|
topRight: Radius.circular(LARGE_SPACE),
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(LARGE_SPACE),
|
|
||||||
topRight: Radius.circular(LARGE_SPACE),
|
|
||||||
),
|
|
||||||
color: getSheetColor(context),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
cupertino: (_, __) => CupertinoPopupSurface(
|
color: getSheetColor(context),
|
||||||
isSurfacePainted: false,
|
),
|
||||||
child: Container(
|
child: SingleChildScrollView(
|
||||||
color: Colors.white,
|
padding: const EdgeInsets.symmetric(
|
||||||
child: Padding(
|
vertical: LARGE_SPACE,
|
||||||
padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE),
|
horizontal: MEDIUM_SPACE,
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
child: child,
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
cupertino: (_, __) => CupertinoPopupSurface(
|
||||||
|
isSurfacePainted: false,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: LARGE_SPACE,
|
||||||
|
horizontal: MEDIUM_SPACE,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user