mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-20 13:43:43 +02:00
feat(availability-overview): create basic layout with options integration
This commit is contained in:
parent
1b6a727305
commit
2465f84886
16 changed files with 586 additions and 481 deletions
|
@ -9,8 +9,11 @@ class App extends StatelessWidget {
|
||||||
const App({super.key});
|
const App({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => const MaterialApp(
|
Widget build(BuildContext context) => MaterialApp(
|
||||||
home: Home(),
|
home: AvailabilityUserStory(
|
||||||
|
userId: "",
|
||||||
|
options: AvailabilityOptions(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +21,16 @@ class Home extends StatelessWidget {
|
||||||
const Home({super.key});
|
const Home({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => availabilityNavigatorUserStory(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
context,
|
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_options.dart";
|
||||||
export "src/config/availability_translations.dart";
|
export "src/config/availability_translations.dart";
|
||||||
export "src/screens/availability_overview.dart";
|
export "src/ui/screens/availability_overview.dart";
|
||||||
export "src/userstory/navigator_userstory.dart";
|
export "src/userstories.dart";
|
||||||
export "src/userstory/userstory_configuration.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/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 that holds all options for the availability userstory
|
||||||
class AvailabilityOptions {
|
class AvailabilityOptions {
|
||||||
/// AvailabilityOptions constructor where everything is optional.
|
/// AvailabilityOptions constructor where everything is optional.
|
||||||
const AvailabilityOptions({
|
AvailabilityOptions({
|
||||||
this.translations = const AvailabilityTranslations.empty(),
|
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
|
/// The translations for the availability userstory
|
||||||
final AvailabilityTranslations translations;
|
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.
|
/// You can copyWith the values to override some default translations.
|
||||||
/// It is recommended to use this constructor.
|
/// It is recommended to use this constructor.
|
||||||
const AvailabilityTranslations({
|
const AvailabilityTranslations({
|
||||||
required this.calendarTitle,
|
required this.appbarTitle,
|
||||||
required this.addAvailableDayButtonText,
|
required this.editAvailabilityButton,
|
||||||
required this.availabilityOverviewTitle,
|
required this.templateLegendTitle,
|
||||||
required this.availabilityDate,
|
required this.overviewScreenTitle,
|
||||||
required this.availabilityHours,
|
required this.createTemplateButton,
|
||||||
required this.availabilityEmptyMessage,
|
|
||||||
required this.availabilitySubmit,
|
|
||||||
required this.availabilitySave,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// AvailabilityTranslations constructor where everything is optional.
|
/// AvailabilityTranslations constructor where everything is optional.
|
||||||
/// This provides default english values for the availability userstory.
|
/// This provides default english values for the availability userstory.
|
||||||
const AvailabilityTranslations.empty({
|
const AvailabilityTranslations.empty({
|
||||||
this.calendarTitle = "Availability",
|
this.appbarTitle = "Availability",
|
||||||
this.addAvailableDayButtonText = "Add availability",
|
this.editAvailabilityButton = "Edit availability",
|
||||||
this.availabilityOverviewTitle = "Overview of your availabilities",
|
this.templateLegendTitle = "Templates",
|
||||||
this.availabilityDate = "Date",
|
this.createTemplateButton = "Create a new template",
|
||||||
this.availabilityHours = "Hours",
|
this.overviewScreenTitle = "Availability",
|
||||||
this.availabilityEmptyMessage =
|
|
||||||
"No availabilities filled in for this month",
|
|
||||||
this.availabilitySubmit = "Submit",
|
|
||||||
this.availabilitySave = "Save",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The title shown above the calendar
|
/// The title shown above the calendar
|
||||||
final String calendarTitle;
|
final String appbarTitle;
|
||||||
|
|
||||||
/// The text shown on the button to add an available day
|
/// The text shown on the button to edit availabilities for a range
|
||||||
final String addAvailableDayButtonText;
|
final String editAvailabilityButton;
|
||||||
|
|
||||||
/// The title for the overview of the availabilities
|
/// The title for the legend template section on the overview screen
|
||||||
final String availabilityOverviewTitle;
|
final String templateLegendTitle;
|
||||||
|
|
||||||
/// The label for the date of an availability
|
/// The title on the overview screen
|
||||||
final String availabilityDate;
|
final String overviewScreenTitle;
|
||||||
|
|
||||||
/// The label for the hours of an availability
|
/// The label on the button to go to the tempalte creation page
|
||||||
final String availabilityHours;
|
final String createTemplateButton;
|
||||||
|
|
||||||
/// 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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
27
packages/flutter_availability/lib/src/routes.dart
Normal file
27
packages/flutter_availability/lib/src/routes.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
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(
|
||||||
|
onBack: () => Navigator.of(context).pop(),
|
||||||
|
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,97 @@
|
||||||
|
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,
|
||||||
|
required this.onBack,
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/// Callback invoked when a user attempts to go back
|
||||||
|
final VoidCallback onBack;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
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/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:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
|
||||||
|
@ -7,24 +7,12 @@ import "package:intl/intl.dart";
|
||||||
class AvailabilityDayOverview extends StatefulWidget {
|
class AvailabilityDayOverview extends StatefulWidget {
|
||||||
///
|
///
|
||||||
const AvailabilityDayOverview({
|
const AvailabilityDayOverview({
|
||||||
required this.userId,
|
|
||||||
required this.service,
|
|
||||||
required this.options,
|
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.onAvailabilitySaved,
|
required this.onAvailabilitySaved,
|
||||||
this.initialAvailability,
|
this.initialAvailability,
|
||||||
super.key,
|
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
|
/// The date for which the availability is being managed
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
|
|
||||||
|
@ -50,7 +38,7 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
||||||
super.initState();
|
super.initState();
|
||||||
_availability = widget.initialAvailability ??
|
_availability = widget.initialAvailability ??
|
||||||
AvailabilityModel(
|
AvailabilityModel(
|
||||||
userId: widget.userId,
|
userId: "",
|
||||||
startDate: widget.date,
|
startDate: widget.date,
|
||||||
endDate: widget.date,
|
endDate: widget.date,
|
||||||
breaks: [],
|
breaks: [],
|
||||||
|
@ -86,6 +74,10 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
|
var userId = availabilityScope.userId;
|
||||||
|
var service = availabilityScope.service;
|
||||||
|
|
||||||
Future<void> updateAvailabilityStart() async {
|
Future<void> updateAvailabilityStart() async {
|
||||||
var selectedTime = await _selectTime(_startDateController);
|
var selectedTime = await _selectTime(_startDateController);
|
||||||
if (selectedTime == null) return;
|
if (selectedTime == null) return;
|
||||||
|
@ -125,15 +117,15 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
||||||
if (_clearAvailableToday) {
|
if (_clearAvailableToday) {
|
||||||
// remove the availability for the user
|
// remove the availability for the user
|
||||||
if (_availability.id != null) {
|
if (_availability.id != null) {
|
||||||
await widget.service.deleteAvailabilityForUser(
|
await service.dataInterface.deleteAvailabilityForUser(
|
||||||
widget.userId,
|
userId,
|
||||||
_availability.id!,
|
_availability.id!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// add an availability for the user
|
// add an availability for the user
|
||||||
await widget.service.createAvailabilityForUser(
|
await service.dataInterface.createAvailabilityForUser(
|
||||||
widget.userId,
|
userId,
|
||||||
_availability,
|
_availability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -143,140 +135,142 @@ class _AvailabilityDayOverviewState extends State<AvailabilityDayOverview> {
|
||||||
}
|
}
|
||||||
|
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
return Padding(
|
return Scaffold(
|
||||||
padding: const EdgeInsets.all(16.0),
|
body: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(16.0),
|
||||||
children: [
|
child: Column(
|
||||||
Text(
|
children: [
|
||||||
DateFormat.yMMMMd().format(widget.date),
|
Text(
|
||||||
style: theme.textTheme.bodyLarge,
|
DateFormat.yMMMMd().format(widget.date),
|
||||||
),
|
style: theme.textTheme.bodyLarge,
|
||||||
Row(
|
),
|
||||||
children: [
|
Row(
|
||||||
Checkbox(
|
children: [
|
||||||
value: _clearAvailableToday,
|
Checkbox(
|
||||||
onChanged: (value) {
|
value: _clearAvailableToday,
|
||||||
setState(() {
|
onChanged: (value) {
|
||||||
_clearAvailableToday = value!;
|
setState(() {
|
||||||
});
|
_clearAvailableToday = value!;
|
||||||
},
|
});
|
||||||
),
|
},
|
||||||
const Text("Clear availability for today"),
|
),
|
||||||
],
|
const Text("Clear availability for today"),
|
||||||
),
|
],
|
||||||
Opacity(
|
),
|
||||||
opacity: _clearAvailableToday ? 0.5 : 1,
|
Opacity(
|
||||||
child: IgnorePointer(
|
opacity: _clearAvailableToday ? 0.5 : 1,
|
||||||
ignoring: _clearAvailableToday,
|
child: IgnorePointer(
|
||||||
child: Column(
|
ignoring: _clearAvailableToday,
|
||||||
children: [
|
child: Column(
|
||||||
Row(
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Expanded(
|
children: [
|
||||||
child: GestureDetector(
|
Expanded(
|
||||||
onTap: updateAvailabilityStart,
|
child: GestureDetector(
|
||||||
child: AbsorbPointer(
|
onTap: updateAvailabilityStart,
|
||||||
child: TextField(
|
child: AbsorbPointer(
|
||||||
controller: _startDateController,
|
child: TextField(
|
||||||
decoration: const InputDecoration(
|
controller: _startDateController,
|
||||||
labelText: "Begin tijd",
|
decoration: const InputDecoration(
|
||||||
suffixIcon: Icon(Icons.access_time),
|
labelText: "Begin tijd",
|
||||||
|
suffixIcon: Icon(Icons.access_time),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 16),
|
||||||
const SizedBox(width: 16),
|
const Text("tot"),
|
||||||
const Text("tot"),
|
const SizedBox(width: 16),
|
||||||
const SizedBox(width: 16),
|
Expanded(
|
||||||
Expanded(
|
child: GestureDetector(
|
||||||
child: GestureDetector(
|
onTap: updateAvailabilityEnd,
|
||||||
onTap: updateAvailabilityEnd,
|
child: AbsorbPointer(
|
||||||
child: AbsorbPointer(
|
child: TextField(
|
||||||
child: TextField(
|
controller: _endDateController,
|
||||||
controller: _endDateController,
|
decoration: const InputDecoration(
|
||||||
decoration: const InputDecoration(
|
labelText: "Eind tijd",
|
||||||
labelText: "Eind tijd",
|
suffixIcon: Icon(Icons.access_time),
|
||||||
suffixIcon: Icon(Icons.access_time),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(
|
||||||
const SizedBox(
|
height: 16,
|
||||||
height: 16,
|
),
|
||||||
),
|
const Text("Add pause (optional)"),
|
||||||
const Text("Add pause (optional)"),
|
ListView(
|
||||||
ListView(
|
shrinkWrap: true,
|
||||||
shrinkWrap: true,
|
children: _availability.breaks.map(
|
||||||
children: _availability.breaks.map(
|
(breakModel) {
|
||||||
(breakModel) {
|
var start =
|
||||||
var start =
|
DateFormat("HH:mm").format(breakModel.startTime);
|
||||||
DateFormat("HH:mm").format(breakModel.startTime);
|
var end =
|
||||||
var end =
|
DateFormat("HH:mm").format(breakModel.endTime);
|
||||||
DateFormat("HH:mm").format(breakModel.endTime);
|
return GestureDetector(
|
||||||
return GestureDetector(
|
onTap: () async {
|
||||||
onTap: () async {
|
var updatedBreak =
|
||||||
var updatedBreak =
|
await AvailabilityBreakSelectionDialog.show(
|
||||||
await AvailabilityBreakSelectionDialog.show(
|
context,
|
||||||
context,
|
breakModel,
|
||||||
breakModel,
|
);
|
||||||
);
|
if (updatedBreak != null) {
|
||||||
if (updatedBreak != null) {
|
setState(() {
|
||||||
setState(() {
|
_availability.breaks.remove(breakModel);
|
||||||
_availability.breaks.remove(breakModel);
|
_availability.breaks.add(updatedBreak);
|
||||||
_availability.breaks.add(updatedBreak);
|
});
|
||||||
});
|
}
|
||||||
}
|
},
|
||||||
},
|
child: Container(
|
||||||
child: Container(
|
decoration: const BoxDecoration(
|
||||||
decoration: const BoxDecoration(
|
color: Colors.lightBlue,
|
||||||
color: Colors.lightBlue,
|
),
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"${breakModel.duration.inMinutes}"
|
||||||
|
" minutes | ",
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"$start - "
|
||||||
|
"$end",
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_availability.breaks.remove(breakModel);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
);
|
||||||
padding: const EdgeInsets.all(16),
|
},
|
||||||
child: Row(
|
).toList(),
|
||||||
children: [
|
),
|
||||||
Text(
|
TextButton(
|
||||||
"${breakModel.duration.inMinutes}"
|
onPressed: onClickAddPause,
|
||||||
" minutes | ",
|
child: const Text("Add"),
|
||||||
),
|
),
|
||||||
Text(
|
],
|
||||||
"$start - "
|
),
|
||||||
"$end",
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_availability.breaks.remove(breakModel);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).toList(),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: onClickAddPause,
|
|
||||||
child: const Text("Add"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Spacer(),
|
||||||
const Spacer(),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
onPressed: () async => onClickSave(),
|
||||||
onPressed: () async => onClickSave(),
|
child: const Text(""),
|
||||||
child: Text(widget.options.translations.availabilitySave),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -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