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 'package:flutter/material.dart';
import 'package:markettakers/main.dart';
import 'package:markettakers/markettakers.dart';
class SimpleStage extends StageFactory {
@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:markettakers/main.dart';
import 'package:rive_native/rive_native.dart';
import 'package:talker_flutter/talker_flutter.dart';
class SimpleStage extends StageFactory {
final String title = "Test";
export 'package:markettakers/src/bootstrapper.dart'
show Bootstrapper, StageFactory, StageController;
@override
Future<bool> get isCompleted async {
return false;
}
final talker = Talker();
@override
Future<void> start(StageController controller) async {
await Future.delayed(Duration(seconds: 2));
controller.updateProgress(0.3);
controller.updateTitle("test 2");
final commonTheme = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
).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:markettakers/main.dart';
import 'package:markettakers/markettakers.dart';
import 'package:markettakers/src/loaders/loader.dart';
import 'package:talker_flutter/talker_flutter.dart';
@@ -18,10 +18,7 @@ class AboutScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Flexible(
flex: 2,
child: Loader(flavour: LoaderFlavour.big, playing: true),
),
Flexible(flex: 2, child: Loader.playing(flavour: LoaderFlavour.big)),
Flexible(
flex: 1,
child: Text(

View File

@@ -1,10 +1,8 @@
import 'dart:async';
import 'dart:isolate';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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';
part 'bootstrapper.freezed.dart';
@@ -48,7 +46,7 @@ class BootstrapState with _$BootstrapState {
final StageController? controller;
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});
}
@@ -81,6 +79,7 @@ class BootstrapController extends Bloc<BootstrapEvent, BootstrapState> {
if (newState.isCompleted) {
talker.info("BootstrapController: All stages completed");
add(const BootstrapEvent.finished());
} else if (await newState.currentStage.isCompleted) {
talker.info(
"BootstrapController: Stage ${newState.currentStage.title} already completed, skipping",
@@ -93,30 +92,28 @@ class BootstrapController extends Bloc<BootstrapEvent, BootstrapState> {
emit(newState);
}
});
on<FinishedEvent>((event, emit) {
talker.info("BootstrapController: Bootstrap process finished");
});
}
StageController launchCurrentStage(Stages stages, int index) {
final currentStage = stages[index];
final controller = StageController(title: currentStage.title);
Isolate.run(
() => currentStage
.start(controller)
.then((_) {
talker.info(
"BootstrapController: Stage ${currentStage.title} completed",
);
add(BootstrapEvent.stageCompleted());
})
.catchError((error) {
talker.handle(
error,
null,
"BootstrapController: Error in ${currentStage.title}",
);
addError(error);
}),
);
currentStage
.start(controller)
.then((_) {
add(BootstrapEvent.stageCompleted());
})
.catchError((error) {
talker.handle(
error,
null,
"BootstrapController: Error in ${currentStage.title}",
);
addError(error);
});
return controller;
}
@@ -136,7 +133,7 @@ class BootstrapFooter extends StatelessWidget {
return Column(
children: [
Text(
"${state.currentStage.title} ${controller.state.progress.toStringAsFixed(2)}%",
"${state.currentStage.title} ${(controller.state.progress * 100).toStringAsFixed(2)}%",
),
CircularProgressIndicator.adaptive(
value: controller.state.progress,
@@ -164,7 +161,6 @@ class Bootstrapper extends StatelessWidget {
create: (context) {
final controller = BootstrapController(BootstrapState(stages));
controller.add(const BootstrapEvent.start());
controller.launchCurrentStage(stages, 0);
return controller;
},
child: BlocListener<BootstrapController, BootstrapState>(
@@ -180,9 +176,7 @@ class Bootstrapper extends StatelessWidget {
children: [
Flexible(
flex: 3,
child: Center(
child: Loader(flavour: LoaderFlavour.big, playing: true),
),
child: Center(child: Loader.playing(flavour: LoaderFlavour.big)),
),
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';
enum LoaderFlavour {
small(filepath: 'assets/loaders/small_loaders/small_loader.riv'),
big(filepath: 'assets/loaders/big_loaders/big_logo_splash.riv');
small(filepath: 'assets/animations/loaders/small_loader.riv'),
big(filepath: 'assets/animations/big_loaders/big_logo_splash.riv');
const LoaderFlavour({required this.filepath});
final String filepath;
}
class Loader extends BaseLoader {
final LoaderFlavour flavour;
final bool playing;
class LoaderController extends Cubit<bool> {
LoaderController() : super(false);
Loader({super.key, required this.flavour, this.playing = false})
: super(filepath: flavour.filepath, fit: Fit.contain);
void show() => emit(true);
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
description: "MarketTakers library"
version: 1.0.0
publish_to: none
version: 1.0.1
publish_to: https://gitea.tailc58da6.ts.net/api/packages/MarketTakers/pub/
repository: https://gitea.tailc58da6.ts.net/MarketTakers/markettakers-flutter
environment:
sdk: ^3.9.2
@@ -23,7 +24,6 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
riverpod_generator: ^3.0.2
build_runner: ^2.7.1
freezed: ^3.2.3
@@ -31,5 +31,5 @@ dev_dependencies:
flutter:
assets:
- assets/animations/big_loaders/
- assets/animations/loaders/
- assets/animations/big_loaders/big_logo_splash.riv
- assets/animations/loaders/small_loader.riv