Compare commits

...

4 Commits

Author SHA1 Message Date
hdbg
85f06c3785 fix(bootstraper): invalid logic 2025-10-10 15:17:05 +02:00
hdbg
d08174c79f fix(deps): removed unused deps 2025-10-09 18:10:45 +02:00
hdbg
e493faaf27 fix!: renamed top-level lib to package name 2025-10-09 18:09:44 +02:00
hdbg
80a4a1126b fix(deps): removed mentions of riverpod 2025-10-09 18:08:37 +02:00
9 changed files with 106 additions and 247 deletions

View File

@@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:markettakers/main.dart'; import 'package:markettakers/markettakers.dart';
class SimpleStage extends StageFactory { class SimpleStage extends StageFactory {
@override @override

View File

@@ -1,19 +0,0 @@
import 'package:flutter/material.dart';
import 'package:rive_native/rive_native.dart';
import 'package:talker_flutter/talker_flutter.dart';
export 'package:markettakers/src/bootstrapper.dart'
show Bootstrapper, StageFactory, StageController;
final talker = Talker();
final commonTheme = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
).copyWith(secondary: Colors.deepPurpleAccent),
);
void init() async {
await RiveNative.init();
}

View File

@@ -1,59 +1,19 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:markettakers/main.dart'; import 'package:rive_native/rive_native.dart';
import 'package:talker_flutter/talker_flutter.dart';
class SimpleStage extends StageFactory { export 'package:markettakers/src/bootstrapper.dart'
final String title = "Test"; show Bootstrapper, StageFactory, StageController;
@override final talker = Talker();
Future<bool> get isCompleted async {
return false;
}
@override final commonTheme = ThemeData(
Future<void> start(StageController controller) async { colorScheme: ColorScheme.fromSeed(
await Future.delayed(Duration(seconds: 2)); seedColor: Colors.deepPurple,
controller.updateProgress(0.3); brightness: Brightness.dark,
controller.updateTitle("test 2"); ).copyWith(secondary: Colors.deepPurpleAccent),
await Future.delayed(Duration(seconds: 2));
controller.updateProgress(0.6);
}
}
class SimpleStage2 extends StageFactory {
final String title = "Test 2";
@override
Future<bool> get isCompleted async {
return false;
}
@override
Future<void> start(StageController controller) async {
await Future.delayed(Duration(seconds: 2));
controller.updateProgress(0.3);
controller.updateTitle("test 5");
await Future.delayed(Duration(seconds: 2));
controller.updateProgress(0.6);
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
init();
final completer = Completer<void>();
completer.future.then((_) {
talker.info("Bootstrapper completed, launching app");
});
runApp(
Scaffold(
body: Bootstrapper(
stages: [SimpleStage(), SimpleStage2()],
onCompleted: completer,
),
),
); );
void init() async {
await RiveNative.init();
} }

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:markettakers/main.dart'; import 'package:markettakers/markettakers.dart';
import 'package:markettakers/src/loaders/loader.dart'; import 'package:markettakers/src/loaders/loader.dart';
import 'package:talker_flutter/talker_flutter.dart'; import 'package:talker_flutter/talker_flutter.dart';
@@ -18,10 +18,7 @@ class AboutScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Flexible( Flexible(flex: 2, child: Loader.playing(flavour: LoaderFlavour.big)),
flex: 2,
child: Loader(flavour: LoaderFlavour.big, playing: true),
),
Flexible( Flexible(
flex: 1, flex: 1,
child: Text( child: Text(

View File

@@ -1,10 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:isolate';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:markettakers/main.dart'; import 'package:markettakers/markettakers.dart';
import 'package:markettakers/src/loaders/loader.dart'; import 'package:markettakers/src/loaders/loader.dart';
part 'bootstrapper.freezed.dart'; part 'bootstrapper.freezed.dart';
@@ -48,7 +46,7 @@ class BootstrapState with _$BootstrapState {
final StageController? controller; final StageController? controller;
StageFactory get currentStage => stages[currentStageIndex]; StageFactory get currentStage => stages[currentStageIndex];
bool get isCompleted => currentStageIndex > stages.length; bool get isCompleted => currentStageIndex >= stages.length;
BootstrapState(this.stages, {this.currentStageIndex = 0, this.controller}); BootstrapState(this.stages, {this.currentStageIndex = 0, this.controller});
} }
@@ -81,6 +79,7 @@ class BootstrapController extends Bloc<BootstrapEvent, BootstrapState> {
if (newState.isCompleted) { if (newState.isCompleted) {
talker.info("BootstrapController: All stages completed"); talker.info("BootstrapController: All stages completed");
add(const BootstrapEvent.finished());
} else if (await newState.currentStage.isCompleted) { } else if (await newState.currentStage.isCompleted) {
talker.info( talker.info(
"BootstrapController: Stage ${newState.currentStage.title} already completed, skipping", "BootstrapController: Stage ${newState.currentStage.title} already completed, skipping",
@@ -93,19 +92,18 @@ class BootstrapController extends Bloc<BootstrapEvent, BootstrapState> {
emit(newState); emit(newState);
} }
}); });
on<FinishedEvent>((event, emit) {
talker.info("BootstrapController: Bootstrap process finished");
});
} }
StageController launchCurrentStage(Stages stages, int index) { StageController launchCurrentStage(Stages stages, int index) {
final currentStage = stages[index]; final currentStage = stages[index];
final controller = StageController(title: currentStage.title); final controller = StageController(title: currentStage.title);
Isolate.run( currentStage
() => currentStage
.start(controller) .start(controller)
.then((_) { .then((_) {
talker.info(
"BootstrapController: Stage ${currentStage.title} completed",
);
add(BootstrapEvent.stageCompleted()); add(BootstrapEvent.stageCompleted());
}) })
.catchError((error) { .catchError((error) {
@@ -115,8 +113,7 @@ class BootstrapController extends Bloc<BootstrapEvent, BootstrapState> {
"BootstrapController: Error in ${currentStage.title}", "BootstrapController: Error in ${currentStage.title}",
); );
addError(error); addError(error);
}), });
);
return controller; return controller;
} }
@@ -136,7 +133,7 @@ class BootstrapFooter extends StatelessWidget {
return Column( return Column(
children: [ children: [
Text( Text(
"${state.currentStage.title} ${controller.state.progress.toStringAsFixed(2)}%", "${state.currentStage.title} ${(controller.state.progress * 100).toStringAsFixed(2)}%",
), ),
CircularProgressIndicator.adaptive( CircularProgressIndicator.adaptive(
value: controller.state.progress, value: controller.state.progress,
@@ -164,7 +161,6 @@ class Bootstrapper extends StatelessWidget {
create: (context) { create: (context) {
final controller = BootstrapController(BootstrapState(stages)); final controller = BootstrapController(BootstrapState(stages));
controller.add(const BootstrapEvent.start()); controller.add(const BootstrapEvent.start());
controller.launchCurrentStage(stages, 0);
return controller; return controller;
}, },
child: BlocListener<BootstrapController, BootstrapState>( child: BlocListener<BootstrapController, BootstrapState>(
@@ -180,9 +176,7 @@ class Bootstrapper extends StatelessWidget {
children: [ children: [
Flexible( Flexible(
flex: 3, flex: 3,
child: Center( child: Center(child: Loader.playing(flavour: LoaderFlavour.big)),
child: Loader(flavour: LoaderFlavour.big, playing: true),
),
), ),
Flexible(flex: 1, child: BootstrapFooter()), Flexible(flex: 1, child: BootstrapFooter()),
], ],

View File

@@ -1,67 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:rive/rive.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'base_loader.g.dart';
@riverpod
class LoaderState extends _$LoaderState {
@override
bool build() {
return false;
}
void stop() {
state = false;
}
void start() {
state = true;
}
}
class BaseLoader extends HookConsumerWidget {
final String filepath;
final Fit fit;
const BaseLoader({super.key, required this.filepath, this.fit = Fit.contain});
@override
Widget build(BuildContext context, WidgetRef ref) {
final loaderState = ref.watch(loaderStateProvider);
final animFile = useMemoized(
() => FileLoader.fromAsset(filepath, riveFactory: Factory.flutter),
);
useEffect(() {
return animFile.dispose;
}, [animFile]);
return RiveWidgetBuilder(
fileLoader: animFile,
builder: (context, state) {
switch (state) {
case RiveLoading():
return const Center(child: CircularProgressIndicator());
case RiveFailed():
final message = state.error.toString();
return ErrorWidget.withDetails(
message: message,
error: FlutterError(message),
);
case RiveLoaded(:final file):
final controller = RiveWidgetController(
file,
stateMachineSelector: StateMachineSelector.byIndex(0),
);
controller.active = loaderState;
return RiveWidget(controller: controller, fit: fit);
}
},
);
}
}

View File

@@ -1,62 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'base_loader.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(LoaderState)
const loaderStateProvider = LoaderStateProvider._();
final class LoaderStateProvider extends $NotifierProvider<LoaderState, bool> {
const LoaderStateProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'loaderStateProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$loaderStateHash();
@$internal
@override
LoaderState create() => LoaderState();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
String _$loaderStateHash() => r'e220e72e138a376390817c6e5a4051c7e2c665b3';
abstract class _$LoaderState extends $Notifier<bool> {
bool build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<bool, bool>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<bool, bool>,
bool,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

View File

@@ -1,19 +1,75 @@
import 'package:markettakers/src/loaders/base_loader.dart'; import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:rive/rive.dart'; import 'package:rive/rive.dart';
enum LoaderFlavour { enum LoaderFlavour {
small(filepath: 'assets/loaders/small_loaders/small_loader.riv'), small(filepath: 'assets/animations/loaders/small_loader.riv'),
big(filepath: 'assets/loaders/big_loaders/big_logo_splash.riv'); big(filepath: 'assets/animations/big_loaders/big_logo_splash.riv');
const LoaderFlavour({required this.filepath}); const LoaderFlavour({required this.filepath});
final String filepath; final String filepath;
} }
class Loader extends BaseLoader { class LoaderController extends Cubit<bool> {
final LoaderFlavour flavour; LoaderController() : super(false);
final bool playing;
Loader({super.key, required this.flavour, this.playing = false}) void show() => emit(true);
: super(filepath: flavour.filepath, fit: Fit.contain); void hide() => emit(false);
}
class Loader extends HookWidget {
final LoaderFlavour flavour;
final Fit fit;
final LoaderController controller;
const Loader(
this.controller, {
super.key,
required this.flavour,
this.fit = Fit.contain,
});
Loader.playing({required LoaderFlavour flavour, Fit fit = Fit.contain})
: this(LoaderController()..show(), flavour: flavour, fit: fit);
Loader.stopped({required LoaderFlavour flavour, Fit fit = Fit.contain})
: this(LoaderController()..hide(), flavour: flavour, fit: fit);
@override
Widget build(BuildContext context) {
final animFile = useMemoized(
() => FileLoader.fromAsset(flavour.filepath, riveFactory: Factory.rive),
);
return RiveWidgetBuilder(
fileLoader: animFile,
builder: (context, state) {
switch (state) {
case RiveLoading():
return const Center(child: CircularProgressIndicator());
case RiveFailed():
final message = state.error.toString();
return ErrorWidget.withDetails(
message: message,
error: FlutterError(message),
);
case RiveLoaded(:final file):
final controller = RiveWidgetController(
file,
stateMachineSelector: StateMachineSelector.byIndex(0),
);
controller.active = this.controller.state;
this.controller.stream.listen((toggle) {
controller.active = toggle;
});
return RiveWidget(controller: controller, fit: fit);
}
},
);
}
} }

View File

@@ -1,7 +1,8 @@
name: markettakers name: markettakers
description: "MarketTakers library" description: "MarketTakers library"
version: 1.0.0 version: 1.0.1
publish_to: none publish_to: https://gitea.tailc58da6.ts.net/api/packages/MarketTakers/pub/
repository: https://gitea.tailc58da6.ts.net/MarketTakers/markettakers-flutter
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2
@@ -23,7 +24,6 @@ dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
riverpod_generator: ^3.0.2
build_runner: ^2.7.1 build_runner: ^2.7.1
freezed: ^3.2.3 freezed: ^3.2.3
@@ -31,5 +31,5 @@ dev_dependencies:
flutter: flutter:
assets: assets:
- assets/animations/big_loaders/ - assets/animations/big_loaders/big_logo_splash.riv
- assets/animations/loaders/ - assets/animations/loaders/small_loader.riv