Browse Source

Migrate uiDesign to Riverpod

master
Kenneth Bruen 2 years ago
parent
commit
240812e261
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 12
      lib/components/loading/loading.dart
  2. 38
      lib/components/refresh_future_builder.dart
  3. 68
      lib/components/select_train_suggestions/select_train_suggestions.dart
  4. 9
      lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart
  5. 9
      lib/components/select_train_suggestions/select_train_suggestions_material.dart
  6. 55
      lib/main.dart
  7. 23
      lib/pages/about/about_page.dart
  8. 7
      lib/pages/about/about_page_cupertino.dart
  9. 7
      lib/pages/about/about_page_material.dart
  10. 13
      lib/pages/main/main_page.dart
  11. 28
      lib/pages/station_arrdep_page/select_station/select_station.dart
  12. 7
      lib/pages/station_arrdep_page/select_station/select_station_cupertino.dart
  13. 7
      lib/pages/station_arrdep_page/select_station/select_station_material.dart
  14. 79
      lib/pages/station_arrdep_page/view_station/view_station.dart
  15. 27
      lib/pages/station_arrdep_page/view_station/view_station_cupertino.dart
  16. 27
      lib/pages/station_arrdep_page/view_station/view_station_material.dart
  17. 41
      lib/pages/train_info_page/select_train/select_train.dart
  18. 7
      lib/pages/train_info_page/select_train/select_train_cupertino.dart
  19. 7
      lib/pages/train_info_page/select_train/select_train_material.dart
  20. 21
      lib/pages/train_info_page/view_train/train_info.dart
  21. 3
      lib/pages/train_info_page/view_train/train_info_cupertino.dart
  22. 3
      lib/pages/train_info_page/view_train/train_info_material.dart
  23. 43
      lib/providers.dart
  24. 98
      pubspec.lock
  25. 1
      pubspec.yaml

12
lib/components/loading/loading.dart

@ -1,19 +1,19 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/loading/loading_cupertino.dart';
import 'package:info_tren/components/loading/loading_material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
class Loading extends StatelessWidget {
class Loading extends ConsumerWidget {
static const defaultText = 'Loading...';
final UiDesign? uiDesign;
final String? text;
const Loading({ Key? key, this.text, this.uiDesign }) : super(key: key);
const Loading({ super.key, this.text, });
@override
Widget build(BuildContext context) {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return LoadingMaterial(text: text ?? defaultText,);

38
lib/components/refresh_future_builder.dart

@ -1,4 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class RefreshFutureBuilder<T> extends StatefulWidget {
final Future<T> Function()? futureCreator;
@ -134,3 +135,40 @@ enum RefreshFutureBuilderState {
refreshing,
refreshError,
}
class RefreshFutureBuilderProviderAdapter<T> extends ConsumerWidget {
final Provider<AsyncValue<T>> futureProvider;
final Widget Function(BuildContext context, Future Function() refresh, Future Function(Future<T> Function()) replaceFuture, RefreshFutureBuilderSnapshot<T> snapshot) builder;
const RefreshFutureBuilderProviderAdapter({required this.futureProvider, required this.builder, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(futureProvider);
return builder(
context,
() async {
ref.invalidate(futureProvider);
},
(_) => throw UnimplementedError('Cannot replace the future when adapting a FutureProvider'),
value.when(
data: (data) => value.isLoading || value.isRefreshing
? RefreshFutureBuilderSnapshot.refresh(data)
: RefreshFutureBuilderSnapshot.withData(data),
error: (error, st) => value.isLoading || value.isRefreshing
? RefreshFutureBuilderSnapshot.refreshError(value.value, value.error, value.stackTrace)
: RefreshFutureBuilderSnapshot.withError(error, st),
loading: () {
if (value.hasValue) {
return RefreshFutureBuilderSnapshot.refresh(value.value, value.error, value.stackTrace);
}
else if (value.hasError) {
return RefreshFutureBuilderSnapshot.refreshError(value.value, value.error, value.stackTrace);
}
return const RefreshFutureBuilderSnapshot.waiting();
},
),
);
}
}

68
lib/components/select_train_suggestions/select_train_suggestions.dart

@ -1,50 +1,70 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/providers.dart';
import 'package:info_tren/utils/default_ui_design.dart';
class SelectTrainSuggestions extends StatefulWidget {
final UiDesign? uiDesign;
class SelectTrainSuggestions extends ConsumerWidget {
final List<TrainsResult> choices;
final String? currentInput;
final void Function(String trainNumber) onTrainSelected;
const SelectTrainSuggestions({Key? key, required this.uiDesign, required this.choices, this.currentInput, required this.onTrainSelected }) : super(key: key);
const SelectTrainSuggestions({required this.choices, this.currentInput, required this.onTrainSelected, super.key, });
@override
SelectTrainSuggestionsState createState() {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch(uiDesign) {
case UiDesign.MATERIAL:
return SelectTrainSuggestionsStateMaterial();
return SelectTrainSuggestionsMaterial(
choices: choices,
onTrainSelected: onTrainSelected,
currentInput: currentInput,
);
case UiDesign.CUPERTINO:
return SelectTrainSuggestionsStateCupertino();
return SelectTrainSuggestionsCupertino(
choices: choices,
onTrainSelected: onTrainSelected,
currentInput: currentInput,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class SelectTrainSuggestionsState extends State<SelectTrainSuggestions> {
String getUseCurrentInputWidgetText(String currentInput) => 'Caută trenul cu numărul ${widget.currentInput}';
abstract class SelectTrainSuggestionsShared extends StatelessWidget {
String getUseCurrentInputWidgetText(String currentInput) => 'Caută trenul cu numărul $currentInput';
Widget getUseCurrentInputWidget(String currentInput, void Function(String) onTrainSelected);
final List<TrainsResult> choices;
final String? currentInput;
final void Function(String trainNumber) onTrainSelected;
const SelectTrainSuggestionsShared({
required this.choices,
this.currentInput,
required this.onTrainSelected,
super.key,
});
@override
Widget build(BuildContext context) {
var slivers = widget.choices.map((c) => c.company).toSet().map((operator) => OperatorAutocompleteSliver(
uiDesign: widget.uiDesign,
var slivers = choices.map((c) => c.company).toSet().map((operator) => OperatorAutocompleteSliver(
operatorName: operator,
trains: widget.choices.where((c) => c.company == operator).toList(),
onTrainSelected: widget.onTrainSelected,
trains: choices.where((c) => c.company == operator).toList(),
onTrainSelected: onTrainSelected,
)).toList();
return CustomScrollView(
slivers: <Widget>[
...slivers,
SliverToBoxAdapter(
child: widget.currentInput != null && int.tryParse(widget.currentInput!) != null ? getUseCurrentInputWidget(widget.currentInput!, widget.onTrainSelected) : Container(),
child: currentInput != null && int.tryParse(currentInput!) != null ? getUseCurrentInputWidget(currentInput!, onTrainSelected) : Container(),
),
SliverToBoxAdapter(
child: Container(
@ -56,16 +76,19 @@ abstract class SelectTrainSuggestionsState extends State<SelectTrainSuggestions>
}
}
class OperatorAutocompleteSliver extends StatelessWidget {
final UiDesign? uiDesign;
class OperatorAutocompleteSliver extends ConsumerWidget {
final String operatorName;
final List<TrainsResult> trains;
final void Function(String) onTrainSelected;
const OperatorAutocompleteSliver({ Key? key, required this.uiDesign, required this.operatorName, required this.trains, required this.onTrainSelected }) : super(key: key);
const OperatorAutocompleteSliver({
super.key,
required this.operatorName,
required this.trains,
required this.onTrainSelected,
});
Widget mapTrainToItem(TrainsResult train) {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget mapTrainToItem(TrainsResult train, UiDesign uiDesign) {
switch (uiDesign) {
case UiDesign.MATERIAL:
return OperatorAutocompleteTileMaterial(
@ -85,7 +108,8 @@ class OperatorAutocompleteSliver extends StatelessWidget {
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
if (trains.isEmpty) {
return SliverToBoxAdapter(child: Container(),);
}
@ -93,14 +117,14 @@ class OperatorAutocompleteSliver extends StatelessWidget {
return SliverPrototypeExtentList(
prototypeItem: Column(
children: <Widget>[
mapTrainToItem(const TrainsResult(company: 'Company', number: '123', rank: 'R')),
mapTrainToItem(const TrainsResult(company: 'Company', number: '123', rank: 'R'), uiDesign),
],
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Column(
children: <Widget>[
mapTrainToItem(trains[index]),
mapTrainToItem(trains[index], uiDesign),
],
);
},

9
lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart

@ -3,7 +3,14 @@ import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/models.dart';
class SelectTrainSuggestionsStateCupertino extends SelectTrainSuggestionsState {
class SelectTrainSuggestionsCupertino extends SelectTrainSuggestionsShared {
const SelectTrainSuggestionsCupertino({
super.key,
required super.choices,
required super.onTrainSelected,
super.currentInput,
});
@override
Widget getUseCurrentInputWidget(String currentInput, void Function(String) onTrainSelected) {
return Column(

9
lib/components/select_train_suggestions/select_train_suggestions_material.dart

@ -2,7 +2,14 @@ import 'package:flutter/material.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/models.dart';
class SelectTrainSuggestionsStateMaterial extends SelectTrainSuggestionsState {
class SelectTrainSuggestionsMaterial extends SelectTrainSuggestionsShared {
const SelectTrainSuggestionsMaterial({
super.key,
required super.choices,
required super.onTrainSelected,
super.currentInput,
});
@override
Widget getUseCurrentInputWidget(String currentInput, void Function(String) onTrainSelected) {
return Column(

55
lib/main.dart

@ -1,54 +1,55 @@
import 'package:flutter/material.dart';
import 'package:info_tren/models.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:info_tren/pages/main/main_page.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
import 'package:info_tren/providers.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
void main() async {
final sharedPreferences = await SharedPreferences.getInstance();
runApp(
const StartPoint(),
ProviderScope(
overrides: [
sharedPreferencesProvider.overrideWithValue(sharedPreferences),
],
child: const StartPoint(),
),
);
}
Map<String, WidgetBuilder> routesByUiDesign(UiDesign uiDesign) => {
Map<String, WidgetBuilder> get routes => {
Navigator.defaultRouteName: (context) {
return MainPage(
uiDesign: uiDesign,
);
return const MainPage();
},
AboutPage.routeName: (context) {
return AboutPage(
uiDesign: uiDesign,
);
return const AboutPage();
},
SelectTrainPage.routeName: (context) {
return SelectTrainPage(
uiDesign: uiDesign,
);
return const SelectTrainPage();
},
TrainInfo.routeName: (context) {
final args = ModalRoute.of(context)!.settings.arguments as TrainInfoArguments;
return TrainInfo(
trainNumber: args.trainNumber,
date: args.date,
uiDesign: uiDesign,
return ProviderScope(
overrides: [
trainInfoArgumentsProvider.overrideWithValue(args),
],
child: const TrainInfo(),
);
},
SelectStationPage.routeName: (context) {
return SelectStationPage(
uiDesign: uiDesign,
);
return const SelectStationPage();
},
ViewStationPage.routeName: (context) {
return ViewStationPage(
stationName: ModalRoute.of(context)!.settings.arguments as String,
uiDesign: uiDesign,
final args = ModalRoute.of(context)!.settings.arguments as ViewStationArguments;
return ProviderScope(
overrides: [
viewStationArgumentsProvider.overrideWithValue(args),
],
child: const ViewStationPage(),
);
},
};
@ -93,7 +94,7 @@ class StartPoint extends StatelessWidget {
useMaterial3: true,
// fontFamily: 'Atkinson Hyperlegible',
),
routes: routesByUiDesign(UiDesign.MATERIAL),
routes: routes,
);
// }
}

23
lib/pages/about/about_page.dart

@ -1,34 +1,37 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/releases.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/about/about_page_cupertino.dart';
import 'package:info_tren/pages/about/about_page_material.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
import 'package:package_info_plus/package_info_plus.dart';
class AboutPage extends StatefulWidget {
final UiDesign? uiDesign;
const AboutPage({Key? key, this.uiDesign}) : super(key: key);
class AboutPage extends ConsumerWidget {
const AboutPage({super.key});
static String routeName = '/about';
@override
State<StatefulWidget> createState() {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return AboutPageStateMaterial();
return const AboutPageMaterial();
case UiDesign.CUPERTINO:
return AboutPageStateCupertino();
return const AboutPageCupertino();
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class AboutPageState extends State<AboutPage> {
abstract class AboutPageShared extends StatefulWidget {
const AboutPageShared({super.key});
}
abstract class AboutPageState extends State<AboutPageShared> {
static const String download = String.fromEnvironment('DOWNLOAD');
final String pageTitle = 'Despre aplicație';

7
lib/pages/about/about_page_cupertino.dart

@ -3,6 +3,13 @@ import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:url_launcher/url_launcher.dart';
class AboutPageCupertino extends StatefulWidget {
const AboutPageCupertino({super.key});
@override
State<AboutPageShared> createState() => AboutPageStateCupertino();
}
class AboutPageStateCupertino extends AboutPageState {
@override
Widget build(BuildContext context) {

7
lib/pages/about/about_page_material.dart

@ -3,6 +3,13 @@ import 'package:flutter/services.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:url_launcher/url_launcher.dart';
class AboutPageMaterial extends StatefulWidget {
const AboutPageMaterial({super.key});
@override
State<AboutPageShared> createState() => AboutPageStateMaterial();
}
class AboutPageStateMaterial extends AboutPageState {
@override
Widget build(BuildContext context) {

13
lib/pages/main/main_page.dart

@ -1,20 +1,19 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:info_tren/pages/main/main_page_cupertino.dart';
import 'package:info_tren/pages/main/main_page_material.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
class MainPage extends StatelessWidget {
final UiDesign? uiDesign;
const MainPage({ Key? key, this.uiDesign }) : super(key: key);
class MainPage extends ConsumerWidget {
const MainPage({super.key,});
@override
Widget build(BuildContext context) {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:

28
lib/pages/station_arrdep_page/select_station/select_station.dart

@ -1,33 +1,36 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_cupertino.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_material.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
import 'package:info_tren/api/stations.dart' as api_stations;
class SelectStationPage extends StatefulWidget {
final UiDesign? uiDesign;
const SelectStationPage({ Key? key, this.uiDesign }) : super(key: key);
class SelectStationPage extends ConsumerWidget {
const SelectStationPage({ super.key });
static String routeName = '/stationArrDep/selectStation';
@override
SelectStationPageState createState() {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return SelectStationPageStateMaterial();
return const SelectStationPageMaterial();
case UiDesign.CUPERTINO:
return SelectStationPageStateCupertino();
return const SelectStationPageCupertino();
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class SelectStationPageState extends State<SelectStationPage> {
abstract class SelectStationPageShared extends StatefulWidget {
const SelectStationPageShared({super.key});
}
abstract class SelectStationPageState extends State<SelectStationPageShared> {
static const pageTitle = 'Plecări/sosiri stație';
static const textFieldLabel = 'Numele stației';
static const roToEn = {
@ -77,7 +80,10 @@ abstract class SelectStationPageState extends State<SelectStationPage> {
}
void onSuggestionSelected(String suggestion) {
Navigator.of(context).pushNamed(ViewStationPage.routeName, arguments: suggestion);
Navigator.of(context).pushNamed(
ViewStationPage.routeName,
arguments: ViewStationArguments(stationName: suggestion),
);
}
}

7
lib/pages/station_arrdep_page/select_station/select_station_cupertino.dart

@ -2,6 +2,13 @@ import 'package:flutter/cupertino.dart';
import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
class SelectStationPageCupertino extends SelectStationPageShared {
const SelectStationPageCupertino({super.key});
@override
State<StatefulWidget> createState() => SelectStationPageStateCupertino();
}
class SelectStationPageStateCupertino extends SelectStationPageState {
@override
Widget build(BuildContext context) {

7
lib/pages/station_arrdep_page/select_station/select_station_material.dart

@ -1,6 +1,13 @@
import 'package:flutter/material.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
class SelectStationPageMaterial extends SelectStationPageShared {
const SelectStationPageMaterial({super.key});
@override
State<StatefulWidget> createState() => SelectStationPageStateMaterial();
}
class SelectStationPageStateMaterial extends SelectStationPageState {
@override
Widget build(BuildContext context) {

79
lib/pages/station_arrdep_page/view_station/view_station.dart

@ -1,33 +1,49 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/station_data.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_cupertino.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_material.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/providers.dart';
import 'package:info_tren/utils/default_ui_design.dart';
class ViewStationPage extends StatefulWidget {
final UiDesign? uiDesign;
final String stationName;
const ViewStationPage({ Key? key, required this.stationName, this.uiDesign }) : super(key: key);
class ViewStationPage extends HookConsumerWidget {
const ViewStationPage({ super.key, });
static String routeName = '/stationArrDep/viewStation';
ValueNotifier<ViewStationPageTab> useTab() => useState(ViewStationPageTab.departures);
@override
ViewStationPageState createState() {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final tab = useTab();
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return ViewStationPageStateMaterial();
return ViewStationPageMaterial(
tab: tab.value,
setTab: (newTab) => tab.value = newTab,
);
case UiDesign.CUPERTINO:
return ViewStationPageStateCupertino();
return ViewStationPageCupertino(
tab: tab.value,
setTab: (newTab) => tab.value = newTab,
);
}
}
}
abstract class ViewStationPageState extends State<ViewStationPage> {
class ViewStationArguments {
final String stationName;
const ViewStationArguments({required this.stationName});
}
abstract class ViewStationPageShared extends StatelessWidget {
static const arrivals = 'Sosiri';
static const departures = 'Plecări';
static const loadingText = 'Se încarcă...';
@ -38,42 +54,15 @@ abstract class ViewStationPageState extends State<ViewStationPage> {
static const departedTo = 'A plecat către';
static const cancelledDeparture = 'Anulat - către';
ViewStationPageTab tab = ViewStationPageTab.departures;
late String stationName;
late Future<StationData> Function() futureCreator;
@override
void initState() {
initData();
super.initState();
}
@override
void didChangeDependencies() {
if (stationName != widget.stationName) {
setState(() {
initData();
});
}
super.didChangeDependencies();
}
void initData() {
stationName = widget.stationName;
futureCreator = () => getStationData(stationName);
}
final ViewStationPageTab tab;
final void Function(ViewStationPageTab) setTab;
const ViewStationPageShared({required this.tab, required this.setTab, super.key});
Widget buildContent(BuildContext context, Future Function() refresh, Future Function(Future<StationData> Function() newFutureBuilder) _, RefreshFutureBuilderSnapshot<StationData> snapshot);
Widget buildStationArrivalItem(BuildContext context, StationArrDep item);
Widget buildStationDepartureItem(BuildContext context, StationArrDep item);
void onTabChange(int index) {
setState(() {
tab = ViewStationPageTab.values[index];
});
}
void onTrainTapped(StationTrain train) {
void onTrainTapped(BuildContext context, StationTrain train) {
Navigator.of(context).pushNamed(
TrainInfo.routeName,
arguments: TrainInfoArguments(
@ -83,10 +72,14 @@ abstract class ViewStationPageState extends State<ViewStationPage> {
);
}
void onTabChange (int index) {
setTab(ViewStationPageTab.values[index]);
}
@override
Widget build(BuildContext context) {
return RefreshFutureBuilder(
futureCreator: futureCreator,
return RefreshFutureBuilderProviderAdapter(
futureProvider: viewStationDataProvider,
builder: buildContent,
);
}

27
lib/pages/station_arrdep_page/view_station/view_station_cupertino.dart

@ -1,27 +1,36 @@
import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
class ViewStationPageCupertino extends ViewStationPageShared {
const ViewStationPageCupertino({super.key, required super.tab, required super.setTab});
class ViewStationPageStateCupertino extends ViewStationPageState {
@override
Widget buildContent(BuildContext context, Future Function() refresh, Future Function(Future<StationData> Function()) _, RefreshFutureBuilderSnapshot<StationData> snapshot) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(snapshot.hasData ? snapshot.data!.stationName : stationName),
middle: Consumer(
builder: (context, ref, _) {
final stationName = ref.watch(viewStationArgumentsProvider.select((value) => value.stationName));
return Text(snapshot.hasData ? snapshot.data!.stationName : stationName);
}
),
),
child: snapshot.hasData ? CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.arrow_down),
label: ViewStationPageState.arrivals,
label: ViewStationPageShared.arrivals,
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.arrow_up),
label: ViewStationPageState.departures,
label: ViewStationPageShared.departures,
),
],
onTap: onTabChange,
@ -47,14 +56,14 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
),
);
},
) : snapshot.state == RefreshFutureBuilderState.waiting ? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,) : Container(),
) : snapshot.state == RefreshFutureBuilderState.waiting ? const Loading(text: ViewStationPageShared.loadingText,) : Container(),
);
}
@override
Widget buildStationArrivalItem(BuildContext context, StationArrDep item) {
return GestureDetector(
onTap: () => onTrainTapped(item.train),
onTap: () => onTrainTapped(context, item.train),
child: CupertinoFormRow(
prefix: Text.rich(
TextSpan(
@ -73,7 +82,7 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
helper: Text.rich(
TextSpan(
children: [
const TextSpan(text: ViewStationPageState.arrivesFrom),
const TextSpan(text: ViewStationPageShared.arrivesFrom),
const TextSpan(text: ' '),
TextSpan(text: item.train.terminus),
],
@ -87,7 +96,7 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
@override
Widget buildStationDepartureItem(BuildContext context, StationArrDep item) {
return GestureDetector(
onTap: () => onTrainTapped(item.train),
onTap: () => onTrainTapped(context, item.train),
child: CupertinoFormRow(
prefix: Text.rich(
TextSpan(
@ -106,7 +115,7 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
helper: Text.rich(
TextSpan(
children: [
const TextSpan(text: ViewStationPageState.departsTo),
const TextSpan(text: ViewStationPageShared.departsTo),
const TextSpan(text: ' '),
TextSpan(text: item.train.terminus),
],

27
lib/pages/station_arrdep_page/view_station/view_station_material.dart

@ -1,22 +1,31 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
class ViewStationPageMaterial extends ViewStationPageShared {
const ViewStationPageMaterial({super.key, required super.tab, required super.setTab});
class ViewStationPageStateMaterial extends ViewStationPageState {
@override
Widget buildContent(BuildContext context, Future Function() refresh, Future Function(Future<StationData> Function()) _, RefreshFutureBuilderSnapshot<StationData> snapshot) {
return Scaffold(
appBar: AppBar(
title: Text(snapshot.hasData ? snapshot.data!.stationName : stationName),
title: Consumer(
builder: (context, ref, _) {
final stationName = ref.watch(viewStationArgumentsProvider.select((value) => value.stationName));
return Text(snapshot.hasData ? snapshot.data!.stationName : stationName);
}
),
centerTitle: true,
),
body: snapshot.state == RefreshFutureBuilderState.waiting
? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,)
? const Loading(text: ViewStationPageShared.loadingText,)
: snapshot.state == RefreshFutureBuilderState.error
? Container()
: CustomScrollView(
@ -36,11 +45,11 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.arrow_downward),
label: ViewStationPageState.arrivals,
label: ViewStationPageShared.arrivals,
),
BottomNavigationBarItem(
icon: Icon(Icons.arrow_upward),
label: ViewStationPageState.departures,
label: ViewStationPageShared.departures,
),
],
currentIndex: tab.index,
@ -51,7 +60,7 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
Widget buildStationItem(BuildContext context, StationArrDep item, {required bool arrival}) {
return InkWell(
onTap: () => onTrainTapped(item.train),
onTap: () => onTrainTapped(context, item.train),
child: Container(
color: item.status.cancelled ? Colors.red.withAlpha(100) : null,
child: Row(
@ -117,10 +126,10 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
children: [
TextSpan(
text: item.status.cancelled
? (arrival ? ViewStationPageState.cancelledArrival : ViewStationPageState.cancelledDeparture)
? (arrival ? ViewStationPageShared.cancelledArrival : ViewStationPageShared.cancelledDeparture)
: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0
? (arrival ? ViewStationPageState.arrivedFrom : ViewStationPageState.departedTo)
: (arrival ? ViewStationPageState.arrivesFrom : ViewStationPageState.departsTo)
? (arrival ? ViewStationPageShared.arrivedFrom : ViewStationPageShared.departedTo)
: (arrival ? ViewStationPageShared.arrivesFrom : ViewStationPageShared.departsTo)
),
const TextSpan(text: ' '),
TextSpan(text: item.train.terminus),

41
lib/pages/train_info_page/select_train/select_train.dart

@ -1,46 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train_cupertino.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train_material.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
import 'package:info_tren/api/trains.dart' as api_trains;
typedef TrainSelectedCallback = Function(int trainNumber);
class SelectTrainPage extends StatefulWidget {
final UiDesign? uiDesign;
const SelectTrainPage({Key? key, this.uiDesign}) : super(key: key);
class SelectTrainPage extends ConsumerWidget {
const SelectTrainPage({super.key});
static String routeName = "/trainInfo/selectTrain";
void onTrainSelected(BuildContext context, String selection) {
selection = selection.characters.takeWhile((char) => List.generate(10, (i) => i.toString()).contains(char)).join();
Navigator.of(context).pushNamed(
TrainInfo.routeName,
arguments: TrainInfoArguments(trainNumber: selection),
);
}
@override
SelectTrainPageState createState() {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch(uiDesign) {
case UiDesign.MATERIAL:
return SelectTrainPageStateMaterial();
return const SelectTrainPageMaterial();
case UiDesign.CUPERTINO:
return SelectTrainPageStateCupertino();
return const SelectTrainPageCupertino();
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class SelectTrainPageState extends State<SelectTrainPage> {
abstract class SelectTrainPageShared extends StatefulWidget {
const SelectTrainPageShared({super.key});
void onTrainSelected(BuildContext context, String selection) {
selection = selection.characters.takeWhile((char) => List.generate(10, (i) => i.toString()).contains(char)).join();
Navigator.of(context).pushNamed(
TrainInfo.routeName,
arguments: TrainInfoArguments(trainNumber: selection),
);
}
}
abstract class SelectTrainPageState extends State<SelectTrainPageShared> {
final String pageTitle = 'Informații despre tren';
final String textFieldLabel = 'Numărul trenului';
@ -102,7 +106,6 @@ abstract class SelectTrainPageState extends State<SelectTrainPage> {
}
Widget get suggestionsList => SelectTrainSuggestions(
uiDesign: widget.uiDesign,
choices: filteredTrains,
onTrainSelected: (trainNumber) => widget.onTrainSelected(context, trainNumber),
currentInput: trainNoController.text,

7
lib/pages/train_info_page/select_train/select_train_cupertino.dart

@ -2,6 +2,13 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
class SelectTrainPageCupertino extends SelectTrainPageShared {
const SelectTrainPageCupertino({super.key});
@override
State<SelectTrainPageShared> createState() => SelectTrainPageStateCupertino();
}
class SelectTrainPageStateCupertino extends SelectTrainPageState {
@override
Widget build(BuildContext context) {

7
lib/pages/train_info_page/select_train/select_train_material.dart

@ -2,6 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
class SelectTrainPageMaterial extends SelectTrainPageShared {
const SelectTrainPageMaterial({super.key});
@override
State<SelectTrainPageShared> createState() => SelectTrainPageStateMaterial();
}
class SelectTrainPageStateMaterial extends SelectTrainPageState {
@override
Widget build(BuildContext context) {

21
lib/pages/train_info_page/view_train/train_info.dart

@ -1,27 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/train_data.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/providers.dart';
class TrainInfo extends StatelessWidget {
class TrainInfo extends ConsumerWidget {
static String routeName = "/trainInfo/display";
final UiDesign? uiDesign;
final String trainNumber;
final DateTime? date;
const TrainInfo({Key? key, required this.trainNumber, this.date, this.uiDesign}): super(key: key);
const TrainInfo({super.key,});
@override
Widget build(BuildContext context) {
final uiDesign = this.uiDesign ?? defaultUiDesign;
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
final args = ref.watch(trainInfoArgumentsProvider);
final trainNumber = args.trainNumber;
final date = args.date;
return RefreshFutureBuilder<TrainData>(
futureCreator: () => getTrain(trainNumber, date: date),
@ -80,9 +80,8 @@ abstract class TrainInfoLoading extends StatelessWidget {
TrainInfoLoading({
required this.title,
String? loadingText,
UiDesign? uiDesign,
super.key,
}) : loadingWidget = Loading(uiDesign: uiDesign, text: loadingText,);
}) : loadingWidget = Loading(text: loadingText,);
}
abstract class TrainInfoError extends StatelessWidget {

3
lib/pages/train_info_page/view_train/train_info_cupertino.dart

@ -11,8 +11,7 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino_
import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingCupertino extends TrainInfoLoading {
TrainInfoLoadingCupertino({required super.title, super.loadingText, super.key,})
: super(uiDesign: UiDesign.CUPERTINO);
TrainInfoLoadingCupertino({required super.title, super.loadingText, super.key,});
@override
Widget build(BuildContext context) {

3
lib/pages/train_info_page/view_train/train_info_material.dart

@ -8,8 +8,7 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_material_D
import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingMaterial extends TrainInfoLoading {
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,})
: super(uiDesign: UiDesign.MATERIAL);
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,});
@override
Widget build(BuildContext context) {

43
lib/providers.dart

@ -0,0 +1,43 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/station_data.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/utils/iterable_extensions.dart';
import 'package:shared_preferences/shared_preferences.dart';
final sharedPreferencesProvider = Provider<SharedPreferences>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
final uiDesignProvider = Provider((ref) {
final sharedPreferences = ref.watch(sharedPreferencesProvider);
final stored = sharedPreferences.getString('uiDesign');
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
return design;
});
Future<void> setUiDesign(Ref ref, UiDesign? design) async {
final sharedPreferences = ref.watch(sharedPreferencesProvider);
if (design != null) {
await sharedPreferences.setString('uiDesign', design.name);
}
else {
await sharedPreferences.remove('uiDesign');
}
ref.invalidate(uiDesignProvider);
}
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
final stationDataProvider = FutureProvider.family((ref, String stationName) => getStationData(stationName));
final viewStationArgumentsProvider = Provider<ViewStationArguments>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
final viewStationDataProvider = Provider((ref) {
final args = ref.watch(viewStationArgumentsProvider);
final data = ref.watch(stationDataProvider(args.stationName));
return data;
});

98
pubspec.lock

@ -354,6 +354,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
@ -368,6 +396,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.4"
pub_semver:
dependency: transitive
description:
@ -396,6 +431,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.15"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.14"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
shelf:
dependency: transitive
description:
@ -590,6 +681,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0+2"
yaml:
dependency: transitive
description:

1
pubspec.yaml

@ -32,6 +32,7 @@ dependencies:
hooks_riverpod: ^2.0.2
freezed_annotation: ^2.2.0
json_annotation: ^4.7.0
shared_preferences: ^2.0.15
dev_dependencies:
# flutter_test:

Loading…
Cancel
Save