mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-20 05:33:44 +02:00
feat(availability-overview): create basic layout with options integration
This commit is contained in:
parent
1b6a727305
commit
0945394004
16 changed files with 584 additions and 481 deletions
|
@ -9,8 +9,11 @@ class App extends StatelessWidget {
|
|||
const App({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const MaterialApp(
|
||||
home: Home(),
|
||||
Widget build(BuildContext context) => MaterialApp(
|
||||
home: AvailabilityUserStory(
|
||||
userId: "",
|
||||
options: AvailabilityOptions(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -18,7 +21,16 @@ class Home extends StatelessWidget {
|
|||
const Home({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => availabilityNavigatorUserStory(
|
||||
context,
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
body: const Center(
|
||||
child: Text("Hello World"),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () async {
|
||||
debugPrint("starting availability user story");
|
||||
await openAvailabilitiesForUser(context, "anonymous", null);
|
||||
debugPrint("finishing availability user story");
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,5 @@ library flutter_availability;
|
|||
|
||||
export "src/config/availability_options.dart";
|
||||
export "src/config/availability_translations.dart";
|
||||
export "src/screens/availability_overview.dart";
|
||||
export "src/userstory/navigator_userstory.dart";
|
||||
export "src/userstory/userstory_configuration.dart";
|
||||
export "src/ui/screens/availability_overview.dart";
|
||||
export "src/userstories.dart";
|
||||
|
|
|
@ -1,12 +1,72 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/config/availability_translations.dart";
|
||||
import "package:flutter_availability/src/service/local_data_interface.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/default_base_screen.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/default_buttons.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
||||
/// Class that holds all options for the availability userstory
|
||||
class AvailabilityOptions {
|
||||
/// AvailabilityOptions constructor where everything is optional.
|
||||
const AvailabilityOptions({
|
||||
AvailabilityOptions({
|
||||
this.translations = const AvailabilityTranslations.empty(),
|
||||
});
|
||||
this.baseScreenBuilder = DefaultBaseScreen.builder,
|
||||
this.primaryButtonBuilder = DefaultPrimaryButton.builder,
|
||||
this.textButtonBuilder = DefaultTextButton.builder,
|
||||
this.spacing = const AvailabilitySpacing(),
|
||||
AvailabilityDataInterface? dataInterface,
|
||||
}) : dataInterface = dataInterface ?? LocalAvailabilityDataInterface();
|
||||
|
||||
/// The translations for the availability userstory
|
||||
final AvailabilityTranslations translations;
|
||||
|
||||
/// The implementation for communicating with the persistance layer
|
||||
final AvailabilityDataInterface dataInterface;
|
||||
|
||||
/// A method to wrap your availability screens with a base frame.
|
||||
///
|
||||
/// If you provide a screen here make sure to use a [Scaffold], as some
|
||||
/// elements require a [Material] or other elements that a [Scaffold]
|
||||
/// provides
|
||||
final BaseScreenBuilder baseScreenBuilder;
|
||||
|
||||
/// A way to provide your own primary button implementation
|
||||
final ButtonBuilder primaryButtonBuilder;
|
||||
|
||||
/// A way to provide your own text button implementation
|
||||
final ButtonBuilder textButtonBuilder;
|
||||
|
||||
/// The spacing between elements
|
||||
final AvailabilitySpacing spacing;
|
||||
}
|
||||
|
||||
/// All configurable paddings and whitespaces withing the userstory
|
||||
class AvailabilitySpacing {
|
||||
/// Creates an AvailabilitySpacing
|
||||
const AvailabilitySpacing({
|
||||
this.sidePadding = 32,
|
||||
this.bottomButtonPadding = 40,
|
||||
});
|
||||
|
||||
/// the padding below the button at the bottom
|
||||
final double bottomButtonPadding;
|
||||
|
||||
/// the padding applied on both sides of the screen
|
||||
final double sidePadding;
|
||||
}
|
||||
|
||||
/// Builder definition for providing a base screen surrounding each page
|
||||
typedef BaseScreenBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
VoidCallback onBack,
|
||||
Widget child,
|
||||
);
|
||||
|
||||
/// Builder definition for providing a button implementation
|
||||
typedef ButtonBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
FutureOr<void>? Function() onPressed,
|
||||
Widget child,
|
||||
);
|
||||
|
|
|
@ -9,76 +9,35 @@ class AvailabilityTranslations {
|
|||
/// You can copyWith the values to override some default translations.
|
||||
/// It is recommended to use this constructor.
|
||||
const AvailabilityTranslations({
|
||||
required this.calendarTitle,
|
||||
required this.addAvailableDayButtonText,
|
||||
required this.availabilityOverviewTitle,
|
||||
required this.availabilityDate,
|
||||
required this.availabilityHours,
|
||||
required this.availabilityEmptyMessage,
|
||||
required this.availabilitySubmit,
|
||||
required this.availabilitySave,
|
||||
required this.appbarTitle,
|
||||
required this.editAvailabilityButton,
|
||||
required this.templateLegendTitle,
|
||||
required this.overviewScreenTitle,
|
||||
required this.createTemplateButton,
|
||||
});
|
||||
|
||||
/// AvailabilityTranslations constructor where everything is optional.
|
||||
/// This provides default english values for the availability userstory.
|
||||
const AvailabilityTranslations.empty({
|
||||
this.calendarTitle = "Availability",
|
||||
this.addAvailableDayButtonText = "Add availability",
|
||||
this.availabilityOverviewTitle = "Overview of your availabilities",
|
||||
this.availabilityDate = "Date",
|
||||
this.availabilityHours = "Hours",
|
||||
this.availabilityEmptyMessage =
|
||||
"No availabilities filled in for this month",
|
||||
this.availabilitySubmit = "Submit",
|
||||
this.availabilitySave = "Save",
|
||||
this.appbarTitle = "Availability",
|
||||
this.editAvailabilityButton = "Edit availability",
|
||||
this.templateLegendTitle = "Templates",
|
||||
this.createTemplateButton = "Create a new template",
|
||||
this.overviewScreenTitle = "Availability",
|
||||
});
|
||||
|
||||
/// The title shown above the calendar
|
||||
final String calendarTitle;
|
||||
final String appbarTitle;
|
||||
|
||||
/// The text shown on the button to add an available day
|
||||
final String addAvailableDayButtonText;
|
||||
/// The text shown on the button to edit availabilities for a range
|
||||
final String editAvailabilityButton;
|
||||
|
||||
/// The title for the overview of the availabilities
|
||||
final String availabilityOverviewTitle;
|
||||
/// The title for the legend template section on the overview screen
|
||||
final String templateLegendTitle;
|
||||
|
||||
/// The label for the date of an availability
|
||||
final String availabilityDate;
|
||||
/// The title on the overview screen
|
||||
final String overviewScreenTitle;
|
||||
|
||||
/// The label for the hours of an availability
|
||||
final String availabilityHours;
|
||||
|
||||
/// The text shown when there are no availabilities filled in for a month
|
||||
final String availabilityEmptyMessage;
|
||||
|
||||
/// The text shown on the button to submit the availabilities
|
||||
final String availabilitySubmit;
|
||||
|
||||
/// The text shown on the button to save a single availability
|
||||
final String availabilitySave;
|
||||
|
||||
/// Method to update the translations with new values
|
||||
AvailabilityTranslations copyWith({
|
||||
String? calendarTitle,
|
||||
String? addAvailableDayButtonText,
|
||||
String? availabilityOverviewTitle,
|
||||
String? availabilityDate,
|
||||
String? availabilityHours,
|
||||
String? availabilityEmptyMessage,
|
||||
String? availabilitySubmit,
|
||||
String? availabilitySave,
|
||||
}) =>
|
||||
AvailabilityTranslations(
|
||||
calendarTitle: calendarTitle ?? this.calendarTitle,
|
||||
addAvailableDayButtonText:
|
||||
addAvailableDayButtonText ?? this.addAvailableDayButtonText,
|
||||
availabilityOverviewTitle:
|
||||
availabilityOverviewTitle ?? this.availabilityOverviewTitle,
|
||||
availabilityDate: availabilityDate ?? this.availabilityDate,
|
||||
availabilityHours: availabilityHours ?? this.availabilityHours,
|
||||
availabilityEmptyMessage:
|
||||
availabilityEmptyMessage ?? this.availabilityEmptyMessage,
|
||||
availabilitySubmit: availabilitySubmit ?? this.availabilitySubmit,
|
||||
availabilitySave: availabilitySave ?? this.availabilitySave,
|
||||
);
|
||||
/// The label on the button to go to the tempalte creation page
|
||||
final String createTemplateButton;
|
||||
}
|
||||
|
|
26
packages/flutter_availability/lib/src/routes.dart
Normal file
26
packages/flutter_availability/lib/src/routes.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/flutter_availability.dart";
|
||||
import "package:flutter_availability/src/ui/screens/template_availability_day_overview.dart";
|
||||
|
||||
///
|
||||
final homePageRoute = MaterialPageRoute(
|
||||
builder: (context) => AvailabilityOverview(
|
||||
onEditDateRange: (range) {
|
||||
Navigator.of(context).push(availabilityViewRoute(range.start));
|
||||
},
|
||||
onViewTemplates: () {},
|
||||
),
|
||||
);
|
||||
|
||||
///
|
||||
MaterialPageRoute availabilityViewRoute(
|
||||
DateTime date,
|
||||
) =>
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AvailabilityDayOverview(
|
||||
date: date,
|
||||
onAvailabilitySaved: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
|
@ -1,174 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/config/availability_options.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
///
|
||||
class AvailabilityOverview extends StatefulWidget {
|
||||
///
|
||||
const AvailabilityOverview({
|
||||
required this.userId,
|
||||
required this.service,
|
||||
required this.options,
|
||||
required this.onDayClicked,
|
||||
required this.onAvailabilityClicked,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The user whose availability is being managed
|
||||
final String userId;
|
||||
|
||||
/// The service to use for managing availability
|
||||
final AvailabilityDataInterface service;
|
||||
|
||||
/// The configuration option for the availability overview
|
||||
final AvailabilityOptions options;
|
||||
|
||||
/// Callback for when the user clicks on a day
|
||||
final void Function(DateTime date) onDayClicked;
|
||||
|
||||
/// Callback for when the user clicks on an availability
|
||||
final void Function(AvailabilityModel availability) onAvailabilityClicked;
|
||||
|
||||
@override
|
||||
State<AvailabilityOverview> createState() => _AvailabilityOverviewState();
|
||||
}
|
||||
|
||||
class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||
Stream<List<AvailabilityModel>>? _availabilityStream;
|
||||
|
||||
void _startLoadingAvailabilities(DateTime start, DateTime end) {
|
||||
setState(() {
|
||||
_availabilityStream = widget.service.getAvailabilityForUser(
|
||||
userId: widget.userId,
|
||||
start: start,
|
||||
end: end,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
widget.options.translations.calendarTitle,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
Expanded(
|
||||
child: _availabilityStream == null
|
||||
? const Center(
|
||||
child: Text("Press the button to load availabilities."),
|
||||
)
|
||||
: StreamBuilder<List<AvailabilityModel>>(
|
||||
stream: _availabilityStream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Text("Error: ${snapshot.error}"),
|
||||
);
|
||||
} else if (!snapshot.hasData ||
|
||||
snapshot.data!.isEmpty) {
|
||||
return const Center(
|
||||
child: Text("No availabilities found."),
|
||||
);
|
||||
} else {
|
||||
var sortedAvailabilities = snapshot.data!
|
||||
..sort(
|
||||
(a, b) => a.startDate.compareTo(b.startDate),
|
||||
);
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
var availability = sortedAvailabilities[index];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"Available from ${DateFormat(
|
||||
"dd-MM-yyyy HH:mm",
|
||||
).format(availability.startDate)} "
|
||||
"\nto \n"
|
||||
"${DateFormat("dd-MM-yyyy HH:mm").format(
|
||||
availability.endDate,
|
||||
)}",
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
if (availability.id == null) {
|
||||
return;
|
||||
}
|
||||
await widget.service
|
||||
.deleteAvailabilityForUser(
|
||||
widget.userId,
|
||||
availability.id!,
|
||||
);
|
||||
},
|
||||
),
|
||||
onTap: () =>
|
||||
widget.onAvailabilityClicked(availability),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
// ask the user to select a date
|
||||
var date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate:
|
||||
DateTime.now().subtract(const Duration(days: 365)),
|
||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
||||
);
|
||||
|
||||
if (date == null) {
|
||||
return;
|
||||
}
|
||||
widget.onDayClicked(date);
|
||||
},
|
||||
child: Text(
|
||||
widget.options.translations.addAvailableDayButtonText,
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
// ask the user to select a date
|
||||
var dateRange = await showDateRangePicker(
|
||||
context: context,
|
||||
firstDate:
|
||||
DateTime.now().subtract(const Duration(days: 365)),
|
||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
||||
);
|
||||
|
||||
if (dateRange == null) {
|
||||
return;
|
||||
}
|
||||
_startLoadingAvailabilities(dateRange.start, dateRange.end);
|
||||
},
|
||||
child: const Text("Load availabilities for a date range"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
||||
///
|
||||
class AvailabilityService {
|
||||
///
|
||||
const AvailabilityService({
|
||||
required this.userId,
|
||||
required this.dataInterface,
|
||||
});
|
||||
|
||||
///
|
||||
final String userId;
|
||||
|
||||
///
|
||||
final AvailabilityDataInterface dataInterface;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
///
|
||||
class AvailabilityOverview extends StatelessWidget {
|
||||
///
|
||||
const AvailabilityOverview({
|
||||
required this.onEditDateRange,
|
||||
required this.onViewTemplates,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Callback for when the user clicks on a day
|
||||
final void Function(DateTimeRange range) onEditDateRange;
|
||||
|
||||
/// Callback for when the user wants to navigate to the overview of templates
|
||||
final VoidCallback onViewTemplates;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var options = availabilityScope.options;
|
||||
var translations = options.translations;
|
||||
var spacing = options.spacing;
|
||||
|
||||
void onBack() {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
var title = Center(
|
||||
child: Text(
|
||||
translations.overviewScreenTitle,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
);
|
||||
|
||||
const calendar = SizedBox(
|
||||
height: 320,
|
||||
child: Placeholder(),
|
||||
);
|
||||
|
||||
const templateLegend = SizedBox(
|
||||
height: 40,
|
||||
child: Placeholder(),
|
||||
);
|
||||
|
||||
var startEditButton = options.primaryButtonBuilder(
|
||||
context,
|
||||
() {
|
||||
onEditDateRange(DateTimeRange(start: DateTime(1), end: DateTime(2)));
|
||||
},
|
||||
Text(translations.editAvailabilityButton),
|
||||
);
|
||||
|
||||
var body = CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
title,
|
||||
const SizedBox(height: 24),
|
||||
calendar,
|
||||
const SizedBox(height: 32),
|
||||
templateLegend,
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
fillOverscroll: false,
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: spacing.sidePadding,
|
||||
).copyWith(
|
||||
bottom: spacing.bottomButtonPadding,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: startEditButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return options.baseScreenBuilder(
|
||||
context,
|
||||
onBack,
|
||||
body,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/config/availability_options.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
|
@ -7,24 +7,12 @@ import "package:intl/intl.dart";
|
|||
class AvailabilityDayOverview extends StatefulWidget {
|
||||
///
|
||||
const AvailabilityDayOverview({
|
||||
required this.userId,
|
||||
required this.service,
|
||||
required this.options,
|
||||
required this.date,
|
||||
required this.onAvailabilitySaved,
|
||||
this.initialAvailability,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The user whose availability is being managed
|
||||
final String userId;
|
||||
|
||||
/// The service to use for managing availability
|
||||
final AvailabilityDataInterface service;
|
||||
|
||||
/// The configuration option for the availability overview
|
||||
final AvailabilityOptions options;
|
||||
|
||||
/// The date for which the availability is being managed
|
||||
final DateTime date;
|
||||
|
||||
|
@ -50,7 +38,7 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
|||
super.initState();
|
||||
_availability = widget.initialAvailability ??
|
||||
AvailabilityModel(
|
||||
userId: widget.userId,
|
||||
userId: "",
|
||||
startDate: widget.date,
|
||||
endDate: widget.date,
|
||||
breaks: [],
|
||||
|
@ -86,6 +74,10 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var userId = availabilityScope.userId;
|
||||
var service = availabilityScope.service;
|
||||
|
||||
Future<void> updateAvailabilityStart() async {
|
||||
var selectedTime = await _selectTime(_startDateController);
|
||||
if (selectedTime == null) return;
|
||||
|
@ -125,15 +117,15 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
|||
if (_clearAvailableToday) {
|
||||
// remove the availability for the user
|
||||
if (_availability.id != null) {
|
||||
await widget.service.deleteAvailabilityForUser(
|
||||
widget.userId,
|
||||
await service.dataInterface.deleteAvailabilityForUser(
|
||||
userId,
|
||||
_availability.id!,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// add an availability for the user
|
||||
await widget.service.createAvailabilityForUser(
|
||||
widget.userId,
|
||||
await service.dataInterface.createAvailabilityForUser(
|
||||
userId,
|
||||
_availability,
|
||||
);
|
||||
}
|
||||
|
@ -143,7 +135,8 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
|||
}
|
||||
|
||||
var theme = Theme.of(context);
|
||||
return Padding(
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
|
@ -274,10 +267,11 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
|||
const Spacer(),
|
||||
ElevatedButton(
|
||||
onPressed: () async => onClickSave(),
|
||||
child: Text(widget.options.translations.availabilitySave),
|
||||
child: const Text(""),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
/// Default base screen for any availability screen
|
||||
class DefaultBaseScreen extends StatelessWidget {
|
||||
|
||||
/// Create a base screen
|
||||
const DefaultBaseScreen({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
/// Builder as default option
|
||||
static Widget builder(
|
||||
BuildContext context,
|
||||
VoidCallback onBack,
|
||||
Widget child,
|
||||
) =>
|
||||
DefaultBaseScreen(child: child);
|
||||
|
||||
/// Content of the page
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var translations = AvailabilityScope.of(context).options.translations;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(translations.appbarTitle),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
///
|
||||
class DefaultPrimaryButton extends StatelessWidget {
|
||||
///
|
||||
const DefaultPrimaryButton({
|
||||
required this.child,
|
||||
required this.onPressed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
static Widget builder(
|
||||
BuildContext context,
|
||||
FutureOr<void> Function()? onPressed,
|
||||
Widget child,
|
||||
) =>
|
||||
DefaultPrimaryButton(
|
||||
onPressed: onPressed,
|
||||
child: child,
|
||||
);
|
||||
|
||||
///
|
||||
final Widget child;
|
||||
|
||||
///
|
||||
final FutureOr<void> Function()? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
FilledButton(onPressed: onPressed, child: child);
|
||||
}
|
||||
|
||||
///
|
||||
class DefaultTextButton extends StatelessWidget {
|
||||
///
|
||||
const DefaultTextButton({
|
||||
required this.child,
|
||||
required this.onPressed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
static Widget builder(
|
||||
BuildContext context,
|
||||
FutureOr<void> Function()? onPressed,
|
||||
Widget child,
|
||||
) =>
|
||||
DefaultTextButton(
|
||||
onPressed: onPressed,
|
||||
child: child,
|
||||
);
|
||||
|
||||
///
|
||||
final Widget child;
|
||||
|
||||
///
|
||||
final FutureOr<void> Function()? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
TextButton(onPressed: onPressed, child: child);
|
||||
}
|
83
packages/flutter_availability/lib/src/userstories.dart
Normal file
83
packages/flutter_availability/lib/src/userstories.dart
Normal file
|
@ -0,0 +1,83 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/config/availability_options.dart";
|
||||
import "package:flutter_availability/src/routes.dart";
|
||||
import "package:flutter_availability/src/service/availability_service.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
/// This pushes the availability user story to the navigator stack.
|
||||
Future<void> openAvailabilitiesForUser(
|
||||
BuildContext context,
|
||||
String userId,
|
||||
AvailabilityOptions? options,
|
||||
) async {
|
||||
var navigator = Navigator.of(context);
|
||||
|
||||
await navigator.push(
|
||||
AvailabilityUserStory.route(
|
||||
userId,
|
||||
options ?? AvailabilityOptions(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
class AvailabilityUserStory extends StatefulWidget {
|
||||
///
|
||||
const AvailabilityUserStory({
|
||||
required this.userId,
|
||||
required this.options,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
final String userId;
|
||||
|
||||
///
|
||||
final AvailabilityOptions options;
|
||||
|
||||
///
|
||||
static MaterialPageRoute route(String userId, AvailabilityOptions options) =>
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AvailabilityUserStory(
|
||||
userId: userId,
|
||||
options: options,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
State<AvailabilityUserStory> createState() => _AvailabilityUserStoryState();
|
||||
}
|
||||
|
||||
class _AvailabilityUserStoryState extends State<AvailabilityUserStory> {
|
||||
late AvailabilityService _service = AvailabilityService(
|
||||
userId: widget.userId,
|
||||
dataInterface: widget.options.dataInterface,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => AvailabilityScope(
|
||||
userId: widget.userId,
|
||||
options: widget.options,
|
||||
service: _service,
|
||||
child: Navigator(
|
||||
onGenerateInitialRoutes: (state, route) => [
|
||||
homePageRoute,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant AvailabilityUserStory oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (oldWidget.userId != widget.userId ||
|
||||
oldWidget.options != widget.options) {
|
||||
setState(() {
|
||||
_service = AvailabilityService(
|
||||
userId: widget.userId,
|
||||
dataInterface: widget.options.dataInterface,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/screens/availability_day_overview.dart";
|
||||
import "package:flutter_availability/src/screens/availability_overview.dart";
|
||||
import "package:flutter_availability/src/service/local_service.dart";
|
||||
import "package:flutter_availability/src/userstory/userstory_configuration.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
||||
///
|
||||
Widget availabilityNavigatorUserStory(
|
||||
BuildContext context, {
|
||||
AvailabiltyUserstoryConfiguration? configuration,
|
||||
}) =>
|
||||
_availabiltyScreenRoute(
|
||||
context,
|
||||
configuration ??
|
||||
AvailabiltyUserstoryConfiguration(
|
||||
service: LocalAvailabilityDataInterface(),
|
||||
getUserId: (_) => "no-user",
|
||||
),
|
||||
);
|
||||
|
||||
Widget _availabiltyScreenRoute(
|
||||
BuildContext context,
|
||||
AvailabiltyUserstoryConfiguration configuration,
|
||||
) =>
|
||||
SafeArea(
|
||||
child: Scaffold(
|
||||
body: AvailabilityOverview(
|
||||
service: configuration.service,
|
||||
options: configuration.options,
|
||||
userId: configuration.getUserId(context),
|
||||
onDayClicked: (date) async => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _avaibiltyDayOverviewRoute(
|
||||
context,
|
||||
configuration,
|
||||
date,
|
||||
null,
|
||||
),
|
||||
),
|
||||
),
|
||||
onAvailabilityClicked: (availability) async =>
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _avaibiltyDayOverviewRoute(
|
||||
context,
|
||||
configuration,
|
||||
availability.startDate,
|
||||
availability,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _avaibiltyDayOverviewRoute(
|
||||
BuildContext context,
|
||||
AvailabiltyUserstoryConfiguration configuration,
|
||||
DateTime date,
|
||||
AvailabilityModel? availability,
|
||||
) =>
|
||||
SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: AvailabilityDayOverview(
|
||||
service: configuration.service,
|
||||
options: configuration.options,
|
||||
userId: configuration.getUserId(context),
|
||||
date: date,
|
||||
initialAvailability: availability,
|
||||
onAvailabilitySaved: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
);
|
|
@ -1,22 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/config/availability_options.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
||||
///
|
||||
class AvailabiltyUserstoryConfiguration {
|
||||
///
|
||||
const AvailabiltyUserstoryConfiguration({
|
||||
required this.service,
|
||||
required this.getUserId,
|
||||
this.options = const AvailabilityOptions(),
|
||||
});
|
||||
|
||||
///
|
||||
final AvailabilityOptions options;
|
||||
|
||||
///
|
||||
final AvailabilityDataInterface service;
|
||||
|
||||
///
|
||||
final Function(BuildContext context) getUserId;
|
||||
}
|
29
packages/flutter_availability/lib/src/util/scope.dart
Normal file
29
packages/flutter_availability/lib/src/util/scope.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter_availability/src/config/availability_options.dart";
|
||||
import "package:flutter_availability/src/service/availability_service.dart";
|
||||
|
||||
///
|
||||
class AvailabilityScope extends InheritedWidget {
|
||||
///
|
||||
const AvailabilityScope({
|
||||
required this.userId,
|
||||
required this.options,
|
||||
required this.service,
|
||||
required super.child,
|
||||
super.key,
|
||||
});
|
||||
///
|
||||
final String userId;
|
||||
///
|
||||
final AvailabilityOptions options;
|
||||
///
|
||||
final AvailabilityService service;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(AvailabilityScope oldWidget) =>
|
||||
oldWidget.userId != userId || options != options;
|
||||
|
||||
///
|
||||
static AvailabilityScope of(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<AvailabilityScope>()!;
|
||||
}
|
Loading…
Reference in a new issue