firka_wear: refactor lib folder structure to match firka

This commit is contained in:
2026-03-01 15:13:51 +01:00
parent 9fc73e3c5c
commit 9f36569d2a
27 changed files with 359 additions and 354 deletions

View File

@@ -0,0 +1,98 @@
import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:isar_community/isar.dart';
import 'package:path_provider/path_provider.dart';
import 'package:firka_wear/data/models/generic_cache_model.dart';
import 'package:firka_wear/data/models/homework_cache_model.dart';
import 'package:firka_wear/data/models/timetable_cache_model.dart';
import 'package:firka_wear/data/models/token_model.dart';
import 'package:firka_wear/l10n/app_localizations.dart';
import 'package:firka_wear/l10n/app_localizations_de.dart';
import 'package:firka_wear/l10n/app_localizations_en.dart';
import 'package:firka_wear/l10n/app_localizations_hu.dart';
import 'package:flutter/material.dart';
import 'package:firka_wear/services/wear_sync_store.dart';
Isar? isarInit;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class DeviceInfo {
String model;
String versionRelease;
String versionSdkInt;
DeviceInfo(this.model, this.versionRelease, this.versionSdkInt);
@override
String toString() {
return "DeviceInfo(model = \"$model\", versionRelease = \"$versionRelease\""
", versionSdkInt = \"$versionSdkInt\"";
}
}
class WearAppInitialization {
final Isar isar;
final WearSyncStore syncStore;
final int tokenCount;
final AppLocalizations l10n;
final DeviceInfo devInfo;
WearAppInitialization({
required this.isar,
required this.syncStore,
required this.tokenCount,
required this.l10n,
required this.devInfo,
});
}
Future<Isar> initDB() async {
if (isarInit != null) return isarInit!;
final dir = await getApplicationDocumentsDirectory();
isarInit = await Isar.open(
[
TokenModelSchema,
GenericCacheModelSchema,
TimetableCacheModelSchema,
HomeworkCacheModelSchema,
],
inspector: true,
directory: dir.path,
);
return isarInit!;
}
AppLocalizations getLang() {
switch (ui.PlatformDispatcher.instance.locale.languageCode) {
case 'hu':
return AppLocalizationsHu();
case 'de':
return AppLocalizationsDe();
default:
return AppLocalizationsEn();
}
}
Future<WearAppInitialization> initializeApp() async {
final isar = await initDB();
final syncStore = WearSyncStore();
await syncStore.load();
const channel = MethodChannel("firka.app/main");
final rawInfo = ((await channel.invokeMethod("get_info")) as String).split(
";",
);
return WearAppInitialization(
isar: isar,
syncStore: syncStore,
tokenCount: await isar.tokenModels.count(),
l10n: getLang(),
devInfo: DeviceInfo(rawInfo[0], rawInfo[1], rawInfo[2]),
);
}

View File

@@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:wear_plus/wear_plus.dart';
import 'package:firka_wear/app/initialization.dart';
import 'package:firka_wear/l10n/app_localizations.dart';
import 'package:firka_wear/ui/theme/style.dart';
import 'package:firka_wear/ui/wear/screens/home/home_screen.dart';
import 'package:firka_wear/ui/wear/screens/login/login_screen.dart';
class WearInitializationScreen extends StatelessWidget {
WearInitializationScreen({super.key});
final Future<WearAppInitialization> _initialization = initializeApp();
@override
Widget build(BuildContext context) {
return FutureBuilder<WearAppInitialization>(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return MaterialApp(
key: ValueKey('firkaErrorPage'),
home: Scaffold(
body: Center(
child: WatchShape(
builder: (context, shape, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Error initializing app: ${snapshot.error}',
style: TextStyle(color: Colors.red),
),
child!,
],
);
},
child: SizedBox(),
),
),
),
);
}
Widget screen;
assert(snapshot.data != null);
var data = snapshot.data!;
if (snapshot.data!.tokenCount == 0) {
screen = WearLoginScreen(data, key: ValueKey('wearLoginScreen'));
} else {
screen = WearHomeScreen(data, key: ValueKey('wearHomeScreen'));
}
return MaterialApp(
key: ValueKey('firkaWearApp'),
title: 'Firka',
navigatorKey: navigatorKey,
theme: ThemeData(
primarySwatch: Colors.lightGreen,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: screen,
routes: {
'/login': (context) =>
WearLoginScreen(data, key: ValueKey('wearLoginScreen')),
'/home': (context) =>
WearHomeScreen(data, key: ValueKey('wearHomeScreen')),
},
);
}
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Container(color: wearStyle.colors.secondary)],
),
),
),
);
},
);
}
}

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../l10n/app_localizations.dart';
import 'package:firka_wear/core/debug_helper.dart';
import 'package:firka_wear/l10n/app_localizations.dart';
import 'package:kreta_api/kreta_api.dart';
import 'debug_helper.dart';
extension IterableExtensionMap on Iterable<MapEntry<String, dynamic>> {
Map<String, dynamic> toMap() {

View File

@@ -1,4 +1,4 @@
import 'package:firka_wear/ui/widget/delayed_spinner.dart';
import 'package:firka_wear/ui/shared/delayed_spinner.dart';
import 'package:flutter/material.dart';
abstract class StatelessAsyncWidget extends StatelessWidget {

View File

@@ -1,7 +1,7 @@
import 'package:isar_community/isar.dart';
import '../../debug_helper.dart';
import '../util.dart';
import 'package:firka_wear/core/debug_helper.dart';
import 'package:firka_wear/data/util.dart';
part 'homework_cache_model.g.dart';

View File

@@ -1,7 +1,7 @@
import 'package:isar_community/isar.dart';
import '../../debug_helper.dart';
import '../util.dart';
import 'package:firka_wear/core/debug_helper.dart';
import 'package:firka_wear/data/util.dart';
part 'timetable_cache_model.g.dart';

View File

@@ -3,7 +3,7 @@ import 'dart:math';
import 'package:intl/intl.dart';
import 'package:isar_community/isar.dart';
import '../debug_helper.dart';
import 'package:firka_wear/core/debug_helper.dart';
class DatedCacheEntry {
Id? cacheKey;

View File

@@ -1,114 +1,13 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:dio/dio.dart';
import 'package:firka_wear/helpers/db/models/generic_cache_model.dart';
import 'package:firka_wear/helpers/db/models/homework_cache_model.dart';
import 'package:firka_wear/helpers/db/models/timetable_cache_model.dart';
import 'package:firka_wear/helpers/db/models/token_model.dart';
import 'package:firka_wear/helpers/sync/wear_sync_store.dart';
import 'package:firka_wear/ui/model/style.dart';
import 'package:firka_wear/ui/wear/screens/login/login_screen.dart';
import 'package:firka_wear/app/initialization_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:isar_community/isar.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wear_plus/wear_plus.dart';
import 'l10n/app_localizations.dart';
import 'l10n/app_localizations_de.dart';
import 'l10n/app_localizations_en.dart';
import 'l10n/app_localizations_hu.dart';
import 'ui/wear/screens/home/home_screen.dart';
Isar? isarInit;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final dio = Dio();
class DeviceInfo {
String model;
String versionRelease;
String versionSdkInt;
DeviceInfo(this.model, this.versionRelease, this.versionSdkInt);
@override
String toString() {
return "DeviceInfo(model = \"$model\", versionRelease = \"$versionRelease\""
", versionSdkInt = \"$versionSdkInt\"";
}
}
class WearAppInitialization {
final Isar isar;
final WearSyncStore syncStore;
final int tokenCount;
final AppLocalizations l10n;
final DeviceInfo devInfo;
WearAppInitialization({
required this.isar,
required this.syncStore,
required this.tokenCount,
required this.l10n,
required this.devInfo,
});
}
Future<Isar> initDB() async {
if (isarInit != null) return isarInit!;
final dir = await getApplicationDocumentsDirectory();
isarInit = await Isar.open(
[
TokenModelSchema,
GenericCacheModelSchema,
TimetableCacheModelSchema,
HomeworkCacheModelSchema,
],
inspector: true,
directory: dir.path,
);
return isarInit!;
}
AppLocalizations getLang() {
switch (ui.window.locale.languageCode) {
case 'hu':
return AppLocalizationsHu();
case 'de':
return AppLocalizationsDe();
default:
return AppLocalizationsEn();
}
}
Future<WearAppInitialization> initializeApp() async {
final isar = await initDB();
final syncStore = WearSyncStore();
await syncStore.load();
const channel = MethodChannel("firka.app/main");
final rawInfo = ((await channel.invokeMethod("get_info")) as String).split(
";",
);
return WearAppInitialization(
isar: isar,
syncStore: syncStore,
tokenCount: await isar.tokenModels.count(),
l10n: getLang(),
devInfo: DeviceInfo(rawInfo[0], rawInfo[1], rawInfo[2]),
);
}
void main() async {
dio.options.connectTimeout = Duration(seconds: 5);
dio.options.receiveTimeout = Duration(seconds: 3);
@@ -126,98 +25,5 @@ void main() async {
await ScreenUtil.ensureScreenSize();
// Run App Initialization
runApp(WearInitializationScreen());
}
class WearInitializationScreen extends StatelessWidget {
WearInitializationScreen({super.key});
// Place to store the initialization future
final Future<WearAppInitialization> _initialization = initializeApp();
@override
Widget build(BuildContext context) {
return FutureBuilder<WearAppInitialization>(
future: _initialization,
builder: (context, snapshot) {
// Check if initialization is complete
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
// Handle initialization error
return MaterialApp(
key: ValueKey('firkaErrorPage'),
home: Scaffold(
body: Center(
child: WatchShape(
builder: (context, shape, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Error initializing app: ${snapshot.error}',
style: TextStyle(color: Colors.red),
),
child!,
],
);
},
child: SizedBox(),
),
),
),
);
}
// Initialization successful, determine which screen to show
Widget screen;
assert(snapshot.data != null);
var data = snapshot.data!;
if (snapshot.data!.tokenCount == 0) {
screen = WearLoginScreen(data, key: ValueKey('wearLoginScreen'));
} else {
screen = WearHomeScreen(data, key: ValueKey('wearHomeScreen'));
}
return MaterialApp(
key: ValueKey('firkaWearApp'),
title: 'Firka',
navigatorKey: navigatorKey,
// Use the global navigator key
theme: ThemeData(
primarySwatch: Colors.lightGreen,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: screen,
routes: {
'/login': (context) =>
WearLoginScreen(data, key: ValueKey('wearLoginScreen')),
'/home': (context) =>
WearHomeScreen(data, key: ValueKey('wearHomeScreen')),
},
);
}
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Container(color: wearStyle.colors.secondary)],
),
),
),
);
},
);
}
}

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import '../../ui/model/style.dart';
import 'package:firka_wear/ui/theme/style.dart';
class FirkaCard extends StatelessWidget {
final List<Widget> left;

View File

@@ -1,7 +1,7 @@
import 'package:kreta_api/kreta_api.dart';
import 'package:flutter/material.dart';
import 'package:kreta_api/kreta_api.dart';
import '../../ui/model/style.dart';
import 'package:firka_wear/ui/theme/style.dart';
import 'grade_helpers.dart';
class GradeWidget extends StatelessWidget {

View File

@@ -1,8 +1,10 @@
import 'dart:ui';
import '../../ui/model/style.dart';
import 'package:flutter/material.dart';
import 'package:kreta_api/kreta_api.dart';
import 'package:firka_wear/ui/theme/style.dart';
int roundGrade(double grade) {
if (grade < 2) {
return 1;

View File

@@ -1,6 +1,7 @@
import 'package:firka_wear/helpers/icon_helper.dart';
import 'package:flutter/material.dart';
import 'package:firka_wear/core/icon_helper.dart';
import 'firka_icon.dart';
class ClassIconWidget extends StatelessWidget {

View File

@@ -1,6 +1,7 @@
import 'package:firka_wear/ui/model/style.dart';
import 'package:flutter/material.dart';
import 'package:firka_wear/ui/theme/style.dart';
class CounterDigitWidget extends StatelessWidget {
final String c;
final TextStyle? style;

View File

@@ -1,3 +1,6 @@
// Enum values match external asset/API naming.
// ignore_for_file: constant_identifier_names
import 'dart:typed_data';
import 'package:flutter/material.dart';

View File

@@ -1,10 +1,10 @@
import 'package:kreta_api/kreta_api.dart';
import 'package:firka_wear/helpers/ui/firka_card.dart';
import 'package:firka_wear/helpers/ui/grade_helpers.dart';
import 'package:firka_wear/ui/widget/class_icon.dart';
import 'package:flutter/material.dart';
import 'package:kreta_api/kreta_api.dart';
import '../model/style.dart';
import 'package:firka_wear/ui/components/firka_card.dart';
import 'package:firka_wear/ui/components/grade_helpers.dart';
import 'package:firka_wear/ui/shared/class_icon.dart';
import 'package:firka_wear/ui/theme/style.dart';
class GradeSmallCard extends FirkaCard {
final List<Grade> grades;

View File

@@ -1,3 +1,6 @@
// Design token names (e.g. H_H1, B_16R) follow the design system.
// ignore_for_file: non_constant_identifier_names
import 'package:flutter/material.dart';
class FirkaFonts {

View File

@@ -1,21 +1,21 @@
import 'dart:async';
import 'dart:math';
import 'package:kreta_api/kreta_api.dart';
import 'package:firka_wear/helpers/extensions.dart';
import 'package:firka_wear/ui/widget/class_icon.dart';
import 'package:firka_wear/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_arc_text/flutter_arc_text.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kreta_api/kreta_api.dart';
import 'package:watch_connectivity/watch_connectivity.dart';
import 'package:wear_plus/wear_plus.dart';
import '../../../../helpers/debug_helper.dart';
import '../../../../l10n/app_localizations.dart';
import '../../../model/style.dart';
import '../../widgets/circular_progress_indicator.dart';
import 'package:firka_wear/app/initialization.dart';
import 'package:firka_wear/core/debug_helper.dart';
import 'package:firka_wear/core/extensions.dart';
import 'package:firka_wear/l10n/app_localizations.dart';
import 'package:firka_wear/ui/theme/style.dart';
import 'package:firka_wear/ui/shared/class_icon.dart';
import 'package:firka_wear/ui/wear/widgets/circular_progress_indicator.dart';
class WearHomeScreen extends StatefulWidget {
final WearAppInitialization data;
@@ -23,13 +23,11 @@ class WearHomeScreen extends StatefulWidget {
const WearHomeScreen(this.data, {super.key});
@override
State<WearHomeScreen> createState() => _WearHomeScreenState(data);
State<WearHomeScreen> createState() => _WearHomeScreenState();
}
class _WearHomeScreenState extends State<WearHomeScreen> {
final WearAppInitialization data;
_WearHomeScreenState(this.data);
WearAppInitialization get data => widget.data;
int? currentLessonNo;
List<Lesson> today = List.empty(growable: true);

View File

@@ -2,28 +2,27 @@
import 'dart:async';
import 'package:kreta_api/kreta_api.dart';
import 'package:firka_wear/helpers/extensions.dart';
import 'package:flutter/material.dart';
import 'package:kreta_api/kreta_api.dart';
import 'package:watch_connectivity/watch_connectivity.dart';
import 'package:wear_plus/wear_plus.dart';
import '../../../../helpers/db/models/token_model.dart';
import '../../../../main.dart';
import '../../../model/style.dart';
import '../home/home_screen.dart';
import 'package:firka_wear/app/initialization.dart';
import 'package:firka_wear/core/extensions.dart';
import 'package:firka_wear/data/models/token_model.dart';
import 'package:firka_wear/ui/theme/style.dart';
import 'package:firka_wear/ui/wear/screens/home/home_screen.dart';
class WearLoginScreen extends StatefulWidget {
final WearAppInitialization data;
const WearLoginScreen(this.data, {super.key});
@override
State<WearLoginScreen> createState() => _WearLoginScreen(data);
State<WearLoginScreen> createState() => _WearLoginScreen();
}
class _WearLoginScreen extends State<WearLoginScreen> {
final WearAppInitialization initData;
_WearLoginScreen(this.initData);
WearAppInitialization get initData => widget.data;
bool init = false;
bool isPaired = false;
@@ -81,7 +80,7 @@ class _WearLoginScreen extends State<WearLoginScreen> {
timetable: timetable,
grades: grades,
);
if (!context.mounted) return;
if (!mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => WearHomeScreen(initData),

View File

@@ -17,11 +17,11 @@ class CircularProgressIndicatorWidget extends StatefulWidget {
});
@override
_CircularProgressIndicatorWidgetState createState() =>
_CircularProgressIndicatorWidgetState();
CircularProgressIndicatorWidgetState createState() =>
CircularProgressIndicatorWidgetState();
}
class _CircularProgressIndicatorWidgetState
class CircularProgressIndicatorWidgetState
extends State<CircularProgressIndicatorWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;