Compare commits
3 commits
0647f260db
...
31aea64608
Author | SHA1 | Date | |
---|---|---|---|
31aea64608 | |||
a5615fe3cb | |||
342b870e93 |
17 changed files with 688 additions and 49 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
v2.7.10
|
||||||
|
Add about page to Fluent UI.
|
||||||
|
Add settings page, allowing changing between UIs.
|
||||||
|
Add touch scrolling on Linux.
|
||||||
|
|
||||||
v2.7.9
|
v2.7.9
|
||||||
Add Fluent UI for Windows and Linux.
|
Add Fluent UI for Windows and Linux.
|
||||||
Add split view in landscape when viewing a train.
|
Add split view in landscape when viewing a train.
|
||||||
|
|
194
codemagic.yaml
Normal file
194
codemagic.yaml
Normal file
File diff suppressed because one or more lines are too long
|
@ -5,39 +5,54 @@ class CupertinoListTile extends StatelessWidget {
|
||||||
final Widget? title;
|
final Widget? title;
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
|
final void Function()? onTap;
|
||||||
|
|
||||||
const CupertinoListTile({ Key? key, this.leading, this.title, this.subtitle, this.trailing, }) : super(key: key);
|
const CupertinoListTile({ super.key, this.leading, this.title, this.subtitle, this.trailing, this.onTap, });
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return GestureDetector(
|
||||||
mainAxisSize: MainAxisSize.max,
|
onTap: onTap,
|
||||||
children: [
|
behavior: HitTestBehavior.opaque,
|
||||||
if (leading != null)
|
child: Row(
|
||||||
leading!,
|
mainAxisSize: MainAxisSize.max,
|
||||||
Expanded(
|
children: [
|
||||||
child: Column(
|
if (leading != null)
|
||||||
mainAxisSize: MainAxisSize.min,
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(8.0),
|
||||||
if (title != null)
|
child: leading!,
|
||||||
title!,
|
),
|
||||||
if (subtitle != null)
|
Expanded(
|
||||||
CupertinoTheme(
|
child: Padding(
|
||||||
data: CupertinoTheme.of(context).copyWith(
|
padding: const EdgeInsets.all(8.0),
|
||||||
textTheme: CupertinoTextThemeData(
|
child: Column(
|
||||||
textStyle: TextStyle(
|
mainAxisSize: MainAxisSize.min,
|
||||||
fontSize: CupertinoTheme.of(context).textTheme.textStyle.fontSize! - 2,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
)
|
children: [
|
||||||
)
|
if (title != null)
|
||||||
),
|
title!,
|
||||||
child: subtitle!,
|
if (subtitle != null)
|
||||||
),
|
CupertinoTheme(
|
||||||
],
|
data: CupertinoTheme.of(context).copyWith(
|
||||||
|
textTheme: CupertinoTextThemeData(
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: CupertinoTheme.of(context).textTheme.textStyle.fontSize! - 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
child: subtitle!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (trailing != null)
|
||||||
if (trailing != null)
|
Padding(
|
||||||
trailing!,
|
padding: const EdgeInsets.all(8.0),
|
||||||
],
|
child: trailing!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as f;
|
import 'package:fluent_ui/fluent_ui.dart' as f;
|
||||||
import 'package:flutter/cupertino.dart' as c;
|
import 'package:flutter/cupertino.dart' as c;
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart' as m;
|
import 'package:flutter/material.dart' as m;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
@ -7,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:info_tren/pages/main/main_page.dart';
|
import 'package:info_tren/pages/main/main_page.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.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/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/view_train/train_info.dart';
|
||||||
|
@ -33,6 +37,9 @@ Map<String, WidgetBuilder> get routes => {
|
||||||
AboutPage.routeName: (context) {
|
AboutPage.routeName: (context) {
|
||||||
return const AboutPage();
|
return const AboutPage();
|
||||||
},
|
},
|
||||||
|
SettingsPage.routeName: (context) {
|
||||||
|
return const SettingsPage();
|
||||||
|
},
|
||||||
SelectTrainPage.routeName: (context) {
|
SelectTrainPage.routeName: (context) {
|
||||||
return const SelectTrainPage();
|
return const SelectTrainPage();
|
||||||
},
|
},
|
||||||
|
@ -59,6 +66,36 @@ Map<String, WidgetBuilder> get routes => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DragFluentScrollBevahior extends f.FluentScrollBehavior {
|
||||||
|
const DragFluentScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DragCupertinoScrollBevahior extends c.CupertinoScrollBehavior {
|
||||||
|
const DragCupertinoScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DragMaterialScrollBevahior extends m.MaterialScrollBehavior {
|
||||||
|
const DragMaterialScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class StartPoint extends ConsumerWidget {
|
class StartPoint extends ConsumerWidget {
|
||||||
final String appTitle = 'Info Tren';
|
final String appTitle = 'Info Tren';
|
||||||
|
|
||||||
|
@ -83,6 +120,7 @@ class StartPoint extends ConsumerWidget {
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
),
|
),
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragCupertinoScrollBevahior() : null,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -95,6 +133,7 @@ class StartPoint extends ConsumerWidget {
|
||||||
accentColor: f.Colors.blue,
|
accentColor: f.Colors.blue,
|
||||||
),
|
),
|
||||||
routes: routes,
|
routes: routes,
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragFluentScrollBevahior() : const f.FluentScrollBehavior(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -110,6 +149,7 @@ class StartPoint extends ConsumerWidget {
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
// fontFamily: 'Atkinson Hyperlegible',
|
// fontFamily: 'Atkinson Hyperlegible',
|
||||||
),
|
),
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragMaterialScrollBevahior() : null,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,14 @@ enum UiDesign {
|
||||||
FLUENT,
|
FLUENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UIName on UiDesign {
|
||||||
|
String get userInterfaceName => (const {
|
||||||
|
UiDesign.MATERIAL: 'Material',
|
||||||
|
UiDesign.CUPERTINO: 'Cupertino',
|
||||||
|
UiDesign.FLUENT: 'Fluent',
|
||||||
|
})[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
class UnmatchedUiDesignException implements Exception {
|
class UnmatchedUiDesignException implements Exception {
|
||||||
final UiDesign uiDesign;
|
final UiDesign uiDesign;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/api/releases.dart';
|
import 'package:info_tren/api/releases.dart';
|
||||||
import 'package:info_tren/models.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_cupertino.dart';
|
||||||
|
import 'package:info_tren/pages/about/about_page_fluent.dart';
|
||||||
import 'package:info_tren/pages/about/about_page_material.dart';
|
import 'package:info_tren/pages/about/about_page_material.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
@ -21,6 +22,8 @@ class AboutPage extends ConsumerWidget {
|
||||||
return const AboutPageMaterial();
|
return const AboutPageMaterial();
|
||||||
case UiDesign.CUPERTINO:
|
case UiDesign.CUPERTINO:
|
||||||
return const AboutPageCupertino();
|
return const AboutPageCupertino();
|
||||||
|
case UiDesign.FLUENT:
|
||||||
|
return const AboutPageFluent();
|
||||||
default:
|
default:
|
||||||
throw UnmatchedUiDesignException(uiDesign);
|
throw UnmatchedUiDesignException(uiDesign);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +34,7 @@ abstract class AboutPageShared extends StatefulWidget {
|
||||||
const AboutPageShared({super.key});
|
const AboutPageShared({super.key});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AboutPageState extends State<AboutPageShared> {
|
abstract class AboutPageState<T extends AboutPageShared> extends State<T> {
|
||||||
static const String download = String.fromEnvironment('DOWNLOAD');
|
static const String download = String.fromEnvironment('DOWNLOAD');
|
||||||
|
|
||||||
final String pageTitle = 'Despre aplicație';
|
final String pageTitle = 'Despre aplicație';
|
||||||
|
|
|
@ -3,14 +3,14 @@ import 'package:info_tren/components/cupertino_divider.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AboutPageCupertino extends StatefulWidget {
|
class AboutPageCupertino extends AboutPageShared {
|
||||||
const AboutPageCupertino({super.key});
|
const AboutPageCupertino({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AboutPageShared> createState() => AboutPageStateCupertino();
|
State<AboutPageShared> createState() => AboutPageStateCupertino();
|
||||||
}
|
}
|
||||||
|
|
||||||
class AboutPageStateCupertino extends AboutPageState {
|
class AboutPageStateCupertino extends AboutPageState<AboutPageCupertino> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CupertinoPageScaffold(
|
return CupertinoPageScaffold(
|
||||||
|
|
140
lib/pages/about/about_page_fluent.dart
Normal file
140
lib/pages/about/about_page_fluent.dart
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class AboutPageFluent extends AboutPageShared {
|
||||||
|
const AboutPageFluent({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AboutPageShared> createState() => AboutPageStateFluent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AboutPageStateFluent extends AboutPageState<AboutPageFluent> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return NavigationView(
|
||||||
|
appBar: NavigationAppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'Info Tren',
|
||||||
|
style: FluentTheme.of(context).typography.display,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (packageInfo != null)
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
packageInfo!.packageName,
|
||||||
|
style: FluentTheme.of(context).typography.caption,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// ListTile(
|
||||||
|
// title: Text(versionTitleText),
|
||||||
|
// subtitle: localChangelog.isEmpty ? null : Text(localChangelog.first.title),
|
||||||
|
// ),
|
||||||
|
const Divider(),
|
||||||
|
for (final log in mergedChangelogs) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
log.version.toString(),
|
||||||
|
style: FluentTheme.of(context).typography.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (localChangelog.isNotEmpty && log.version == localChangelog.first.version)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: FluentTheme.of(context).inactiveColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
currentVersionText,
|
||||||
|
style: const TextStyle(
|
||||||
|
inherit: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (remoteChangelog.isNotEmpty && log.version == remoteChangelog.first.version && (localChangelog.isEmpty || localChangelog.first.version != log.version))
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.green,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
latestVersionText,
|
||||||
|
style: TextStyle(
|
||||||
|
inherit: true,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (AboutPageState.download == 'apk' && log.apkLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.apkLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download APK',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(FluentIcons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text: log.description,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,14 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AboutPageMaterial extends StatefulWidget {
|
class AboutPageMaterial extends AboutPageShared {
|
||||||
const AboutPageMaterial({super.key});
|
const AboutPageMaterial({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AboutPageShared> createState() => AboutPageStateMaterial();
|
State<AboutPageShared> createState() => AboutPageStateMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
class AboutPageStateMaterial extends AboutPageState {
|
class AboutPageStateMaterial extends AboutPageState<AboutPageMaterial> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -138,4 +138,4 @@ class AboutPageStateMaterial extends AboutPageState {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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_cupertino.dart';
|
||||||
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
||||||
import 'package:info_tren/pages/main/main_page_material.dart';
|
import 'package:info_tren/pages/main/main_page_material.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.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/pages/train_info_page/select_train/select_train.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
@ -36,6 +37,12 @@ abstract class MainPageShared extends StatelessWidget {
|
||||||
const MainPageShared({super.key});
|
const MainPageShared({super.key});
|
||||||
|
|
||||||
List<MainPageAction> get popupMenu => [
|
List<MainPageAction> get popupMenu => [
|
||||||
|
MainPageAction(
|
||||||
|
name: 'Setări',
|
||||||
|
action: (context) {
|
||||||
|
Navigator.of(context).pushNamed(SettingsPage.routeName);
|
||||||
|
},
|
||||||
|
),
|
||||||
MainPageAction(
|
MainPageAction(
|
||||||
name: 'Despre aplicație',
|
name: 'Despre aplicație',
|
||||||
action: (context) {
|
action: (context) {
|
||||||
|
@ -85,5 +92,9 @@ class MainPageAction {
|
||||||
final String? description;
|
final String? description;
|
||||||
final void Function(BuildContext context)? action;
|
final void Function(BuildContext context)? action;
|
||||||
|
|
||||||
MainPageAction({required this.name, this.action, this.description});
|
MainPageAction({
|
||||||
|
required this.name,
|
||||||
|
this.action,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,22 @@ class MainPageFluent extends MainPageShared {
|
||||||
appBar: NavigationAppBar(
|
appBar: NavigationAppBar(
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
title: Text(pageTitle),
|
title: Text(pageTitle),
|
||||||
|
actions: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: popupMenu.map((i) => Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon({
|
||||||
|
'Setări': FluentIcons.settings,
|
||||||
|
'Despre aplicație': FluentIcons.info,
|
||||||
|
}[i.name]),
|
||||||
|
onPressed: i.action != null ? () => i.action!(context) : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)).toList(),
|
||||||
|
),
|
||||||
// centerTitle: true,
|
// centerTitle: true,
|
||||||
// actions: [
|
// actions: [
|
||||||
// PopupMenuButton<int>(
|
// PopupMenuButton<int>(
|
||||||
|
|
37
lib/pages/settings/setings_page.dart
Normal file
37
lib/pages/settings/setings_page.dart
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_cupertino.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_fluent.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_material.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPage extends ConsumerWidget {
|
||||||
|
const SettingsPage({super.key,});
|
||||||
|
|
||||||
|
static const String routeName = '/settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final uiDesign = ref.watch(uiDesignProvider);
|
||||||
|
|
||||||
|
switch (uiDesign) {
|
||||||
|
case UiDesign.MATERIAL:
|
||||||
|
return const SettingsPageMaterial();
|
||||||
|
case UiDesign.CUPERTINO:
|
||||||
|
return const SettingsPageCupertino();
|
||||||
|
case UiDesign.FLUENT:
|
||||||
|
return const SettingsPageFluent();
|
||||||
|
default:
|
||||||
|
throw UnmatchedUiDesignException(uiDesign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SettingsPageShared extends StatelessWidget {
|
||||||
|
final String pageTitle = 'Setări';
|
||||||
|
final String appearanceTitle = 'Aspect';
|
||||||
|
|
||||||
|
const SettingsPageShared({super.key});
|
||||||
|
|
||||||
|
}
|
77
lib/pages/settings/settings_page_cupertino.dart
Normal file
77
lib/pages/settings/settings_page_cupertino.dart
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/components/cupertino_listtile.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageCupertino extends SettingsPageShared {
|
||||||
|
const SettingsPageCupertino({super.key,});
|
||||||
|
|
||||||
|
Future<T?> singleChoice<T>({required BuildContext context, required List<T> choices, required String Function(T) labelBuilder, String? title}) async {
|
||||||
|
return await showCupertinoModalPopup<T>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return CupertinoActionSheet(
|
||||||
|
title: title != null ? Text(title) : null,
|
||||||
|
actions: choices.map((c) => CupertinoActionSheetAction(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(c);
|
||||||
|
},
|
||||||
|
child: Text(labelBuilder(c)),
|
||||||
|
)).toList(),
|
||||||
|
cancelButton: CupertinoActionSheetAction(
|
||||||
|
child: const Text('Anulare'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CupertinoPageScaffold(
|
||||||
|
navigationBar: CupertinoNavigationBar(
|
||||||
|
previousPageTitle: 'Info Tren',
|
||||||
|
middle: Text(pageTitle),
|
||||||
|
),
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final mq = MediaQuery.of(context);
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: mq.padding.top,
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return CupertinoListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: Text(currentUiDesign.userInterfaceName),
|
||||||
|
onTap: () async {
|
||||||
|
final choice = await singleChoice(
|
||||||
|
context: context,
|
||||||
|
choices: UiDesign.values,
|
||||||
|
labelBuilder: (UiDesign ud) => ud.userInterfaceName,
|
||||||
|
title: appearanceTitle,
|
||||||
|
);
|
||||||
|
if (choice != null) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(choice);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
42
lib/pages/settings/settings_page_fluent.dart
Normal file
42
lib/pages/settings/settings_page_fluent.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageFluent extends SettingsPageShared {
|
||||||
|
const SettingsPageFluent({super.key,});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return NavigationView(
|
||||||
|
appBar: NavigationAppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: ComboBox<UiDesign>(
|
||||||
|
items: UiDesign.values.map((d) => ComboBoxItem(
|
||||||
|
value: d,
|
||||||
|
child: Text(d.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentUiDesign,
|
||||||
|
onChanged: (newUiDesign) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
43
lib/pages/settings/settings_page_material.dart
Normal file
43
lib/pages/settings/settings_page_material.dart
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageMaterial extends SettingsPageShared {
|
||||||
|
const SettingsPageMaterial({super.key,});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: DropdownButton<UiDesign>(
|
||||||
|
items: UiDesign.values.map((d) => DropdownMenuItem(
|
||||||
|
value: d,
|
||||||
|
child: Text(d.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentUiDesign,
|
||||||
|
onChanged: (newUiDesign) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,22 +13,30 @@ final sharedPreferencesProvider = Provider<SharedPreferences>(
|
||||||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||||
);
|
);
|
||||||
|
|
||||||
final uiDesignProvider = Provider((ref) {
|
class UiDesignNotifier extends StateNotifier<UiDesign> {
|
||||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
final SharedPreferences sharedPreferences;
|
||||||
final stored = sharedPreferences.getString('uiDesign');
|
|
||||||
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
|
UiDesignNotifier({required this.sharedPreferences,}) : super(UiDesign.MATERIAL) {
|
||||||
return design;
|
final stored = sharedPreferences.getString('uiDesign');
|
||||||
});
|
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
|
||||||
Future<void> setUiDesign(Ref ref, UiDesign? design) async {
|
state = design;
|
||||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
|
||||||
if (design != null) {
|
|
||||||
await sharedPreferences.setString('uiDesign', design.name);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
await sharedPreferences.remove('uiDesign');
|
void set(UiDesign? design) async {
|
||||||
|
if (design != null) {
|
||||||
|
await sharedPreferences.setString('uiDesign', design.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await sharedPreferences.remove('uiDesign');
|
||||||
|
}
|
||||||
|
state = design ?? defaultUiDesign;
|
||||||
}
|
}
|
||||||
ref.invalidate(uiDesignProvider);
|
|
||||||
}
|
}
|
||||||
|
final uiDesignProvider = StateNotifierProvider<UiDesignNotifier, UiDesign>(
|
||||||
|
(ref) => UiDesignNotifier(
|
||||||
|
sharedPreferences: ref.watch(sharedPreferencesProvider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
|
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
|
||||||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||||
|
|
|
@ -11,7 +11,7 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 2.7.9
|
version: 2.7.10
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
|
Loading…
Add table
Reference in a new issue