Commit 4b409ce
Changed files (2)
lib
screens
lib/screens/loading.dart
@@ -0,0 +1,108 @@
+import 'dart:math';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+
+/// Loading page that is displayed on app start.
+class LoadingScreen extends StatelessWidget {
+ const LoadingScreen({super.key});
+
+ static const _duration = Duration(milliseconds: 250);
+
+ @override
+ Widget build(BuildContext context) {
+ final dimensions = MediaQuery.of(context).size;
+ return MaterialApp(
+ home: Center(
+ child: TweenAnimationBuilder(
+ tween: Tween<double>(begin: 0, end: 1),
+ duration: _duration,
+ builder: (BuildContext context, double value, Widget? child) {
+ return Container(
+ padding: const EdgeInsets.only(bottom: 100),
+ child: SizedBox.square(
+ dimension: dimensions.width - 20,
+ child: CustomPaint(
+ painter: _LogoPainter(progress: value),
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
+
+/// Paints the logo of the App.
+class _LogoPainter extends CustomPainter {
+ _LogoPainter({required this.progress});
+
+ /// Percentage of the logo to be drawn (ranges from 0 to 1).
+ final double progress;
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ Paint paint = Paint()
+ ..color = const Color.fromARGB(255, 0xb0, 0x18, 0x22)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = size.shortestSide / 20
+ ..strokeCap = StrokeCap.round
+ ..strokeJoin = StrokeJoin.round;
+
+ final path = Path();
+ path.moveTo(size.width * 0.5, size.height * 0.8);
+ path.lineTo(size.width * 0.2, size.height * 0.5);
+ path.lineTo(size.width * 0.15, size.height * 0.4);
+ path.lineTo(size.width * 0.27, size.height * 0.3);
+ path.lineTo(size.width * 0.37, size.height * 0.31);
+ path.lineTo(size.width * 0.5, size.height * 0.43);
+ path.lineTo(size.width * 0.63, size.height * 0.32);
+ path.lineTo(size.width * 0.74, size.height * 0.31);
+ path.lineTo(size.width * 0.76, size.height * 0.32);
+ path.lineTo(size.width * 0.82, size.height * 0.42);
+ path.lineTo(size.width * 0.818, size.height * 0.45);
+ path.lineTo(size.width * 0.65, size.height * 0.65);
+ canvas.drawPath(_subPath(path, progress), paint);
+ }
+
+ @override
+ bool shouldRepaint(_LogoPainter oldDelegate) =>
+ oldDelegate.progress != progress;
+
+ /// Get a percentage of [originalPath].
+ ///
+ /// [animationPercent] is a value from 0 to 1.
+ Path _subPath(Path originalPath, double animationPercent,) {
+ final totalLength = originalPath
+ .computeMetrics()
+ .fold(0.0, (double prev, PathMetric metric) => prev + metric.length);
+
+ return _extractPathUntilLength(originalPath, totalLength * animationPercent);
+ }
+
+ Path _extractPathUntilLength(Path originalPath, double length,) {
+ final path = Path();
+
+ var metricsIterator = originalPath.computeMetrics().iterator;
+ var isLastSegment = false;
+ var currentLength = 0.0;
+
+ while (metricsIterator.moveNext() && !isLastSegment) {
+ var metric = metricsIterator.current;
+
+ var nextLength = currentLength + metric.length;
+ isLastSegment = (nextLength > length);
+
+ assert(length - currentLength >= 0);
+ final pathSegment = metric.extractPath(0.0,
+ min(length - currentLength, metric.length));
+
+ path.addPath(pathSegment, Offset.zero);
+
+ currentLength = nextLength;
+ }
+
+ return path;
+ }
+}
\ No newline at end of file
lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/storage/db/config_dao.dart';
import 'package:blood_pressure_app/model/storage/db/config_db.dart';
@@ -5,6 +6,7 @@ import 'package:blood_pressure_app/model/storage/intervall_store.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:blood_pressure_app/model/storage/update_legacy_settings.dart';
import 'package:blood_pressure_app/screens/home.dart';
+import 'package:blood_pressure_app/screens/loading.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
@@ -17,6 +19,15 @@ late final ConfigDB _database;
late final BloodPressureModel _bloodPressureModel;
void main() async {
+ runApp(ConsistentFutureBuilder(
+ future: _loadApp(),
+ onWaiting: const LoadingScreen(),
+ onData: (context, widget) => widget
+ ));
+}
+
+/// Load the primary app data asynchronously to allow adding load animations.
+Future<Widget> _loadApp() async {
WidgetsFlutterBinding.ensureInitialized();
// 2 different db files
_bloodPressureModel = await BloodPressureModel.create();
@@ -37,17 +48,16 @@ void main() async {
// Reset the step size intervall to current on startup
intervalStorageManager.mainPage.setToMostRecentIntervall();
- runApp(MultiProvider(providers: [
+ return MultiProvider(providers: [
ChangeNotifierProvider(create: (context) => _bloodPressureModel),
ChangeNotifierProvider(create: (context) => settings),
ChangeNotifierProvider(create: (context) => exportSettings),
ChangeNotifierProvider(create: (context) => csvExportSettings),
ChangeNotifierProvider(create: (context) => pdfExportSettings),
ChangeNotifierProvider(create: (context) => intervalStorageManager),
- ], child: const AppRoot()));
+ ], child: const AppRoot());
}
-// TODO: centralize disabling
class AppRoot extends StatelessWidget {
const AppRoot({super.key});