From 49965fc3fcee70870743561683594b36460559a4 Mon Sep 17 00:00:00 2001 From: hdbg Date: Thu, 9 Oct 2025 17:50:04 +0200 Subject: [PATCH] misc: initial commit --- .gitignore | 31 + .metadata | 10 + CHANGELOG.md | 3 + LICENSE | 1 + README.md | 39 ++ analysis_options.yaml | 4 + .../big_loaders/big_logo_splash.riv | Bin 0 -> 26184 bytes assets/animations/loaders/small_loader.riv | Bin 0 -> 1283 bytes assets/images/logo/icon_machinery.svg | 91 +++ example/bootstraper.dart | 61 ++ lib/main.dart | 19 + lib/markettakers.dart | 59 ++ lib/src/about_screen.dart | 98 +++ lib/src/bootstrapper.dart | 193 ++++++ lib/src/bootstrapper.freezed.dart | 649 ++++++++++++++++++ lib/src/error_screen.dart | 44 ++ lib/src/loaders/base_loader.dart | 67 ++ lib/src/loaders/base_loader.g.dart | 62 ++ lib/src/loaders/loader.dart | 19 + pubspec.yaml | 42 ++ 20 files changed, 1492 insertions(+) create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 assets/animations/big_loaders/big_logo_splash.riv create mode 100644 assets/animations/loaders/small_loader.riv create mode 100644 assets/images/logo/icon_machinery.svg create mode 100644 example/bootstraper.dart create mode 100644 lib/main.dart create mode 100644 lib/markettakers.dart create mode 100644 lib/src/about_screen.dart create mode 100644 lib/src/bootstrapper.dart create mode 100644 lib/src/bootstrapper.freezed.dart create mode 100644 lib/src/error_screen.dart create mode 100644 lib/src/loaders/base_loader.dart create mode 100644 lib/src/loaders/base_loader.g.dart create mode 100644 lib/src/loaders/loader.dart create mode 100644 pubspec.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd5eb98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins-dependencies +/build/ +/coverage/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..b3d8d13 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "d693b4b9dbac2acd4477aea4555ca6dcbea44ba2" + channel: "stable" + +project_type: package diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a260d8 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/assets/animations/big_loaders/big_logo_splash.riv b/assets/animations/big_loaders/big_logo_splash.riv new file mode 100644 index 0000000000000000000000000000000000000000..e5696db0dca35311104facf0ff98ae739d15ff67 GIT binary patch literal 26184 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 literal 0 HcmV?d00001 diff --git a/assets/animations/loaders/small_loader.riv b/assets/animations/loaders/small_loader.riv new file mode 100644 index 0000000000000000000000000000000000000000..85bd590c8a4fee8beddb2132b7b617b0e16050f1 GIT binary patch literal 1283 zcmZXTPe>F|9LIle_l@fzwbaT9QVF&|vSk@IGt-%!DMeTVYyX6Fa5D!B%TS9vwHVnS z6Z0UpOF~5_gRq2%AXY~sgxy0zbnwz4*S{D_r%wIezS(Wp=JY<_-=Ft;-}mNRqyLNz zemuT9M>fc&b%`eEGF_pobd9dl4Z20QDeV0A1IYtuHSz&M(K!M(07c6MaB7>@lN$tR zuDiSUQdhA1GFSw_)L$(1lYxYVNeD@Rj|Nr!5Si#asq-tOX8k!Rg|X2jU++n z-cE|gd_1NnMpZGJB*{9RQI|ykN8LOIU86euB_K&f3NSq$b25F*@P6&T9reZisIyZ7+)%LBz>Jwi3^DEr8~&DS#nXYx!QXOH zkA(LbqMjHxyv%){uIKQslGMp{z8hWx*Y(6*19uxSyrp+OMtu!8gIte@6D)s7^*^j$ zlmqW)@0-(kp0WIK)yt;F{sUYGXydw{?Hl;lUvDr(2#u)TvFG1q-k+T&5-!I6`Uid* zT~E{@xm5q;y9aWha&MtIMfk0P-X2%IO{2Mr@76*A_d?g7xbZ#~`0beVa>n#&fyFXO zl1j|C0RnovgI@5uc+AdXnG^{h%!-wzyvIyEF|4AO^Ku{(?lZlKxtX5wCap(Ae4k9` z-el;nc^~w0UiM|{($uiw|Mul`G!S`{>YsY&{B9+wM`1rg@-J}UyZOnyT4kO%&|k6c zKhoGsPiG<(3a4O?P+KuyJ7F9GAQ}`2uES4yxx0gGi`Vp~tf_?4XxR${^Ml7H} J?I;1LgTExKN$UUr literal 0 HcmV?d00001 diff --git a/assets/images/logo/icon_machinery.svg b/assets/images/logo/icon_machinery.svg new file mode 100644 index 0000000..5168436 --- /dev/null +++ b/assets/images/logo/icon_machinery.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/bootstraper.dart b/example/bootstraper.dart new file mode 100644 index 0000000..bce590a --- /dev/null +++ b/example/bootstraper.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:markettakers/main.dart'; + +class SimpleStage extends StageFactory { + @override + final String title = "Test"; + + @override + Future get isCompleted async { + return false; + } + + @override + Future start(StageController controller) async { + await Future.delayed(Duration(seconds: 2)); + controller.updateProgress(0.3); + controller.updateTitle("test 2"); + + await Future.delayed(Duration(seconds: 2)); + controller.updateProgress(0.6); + } +} + +class SimpleStage2 extends StageFactory { + @override + final String title = "Test 2"; + + @override + Future get isCompleted async { + return false; + } + + @override + Future 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(); + completer.future.then((_) { + talker.info("Bootstrapper completed, launching app"); + }); + runApp( + Scaffold( + body: Bootstrapper( + stages: [SimpleStage(), SimpleStage2()], + onCompleted: completer, + ), + ), + ); +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..5b990d9 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,19 @@ +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(); +} diff --git a/lib/markettakers.dart b/lib/markettakers.dart new file mode 100644 index 0000000..66e199f --- /dev/null +++ b/lib/markettakers.dart @@ -0,0 +1,59 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:markettakers/main.dart'; + +class SimpleStage extends StageFactory { + final String title = "Test"; + + @override + Future get isCompleted async { + return false; + } + + @override + Future start(StageController controller) async { + await Future.delayed(Duration(seconds: 2)); + controller.updateProgress(0.3); + controller.updateTitle("test 2"); + + await Future.delayed(Duration(seconds: 2)); + controller.updateProgress(0.6); + } +} + +class SimpleStage2 extends StageFactory { + final String title = "Test 2"; + + @override + Future get isCompleted async { + return false; + } + + @override + Future 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(); + completer.future.then((_) { + talker.info("Bootstrapper completed, launching app"); + }); + runApp( + Scaffold( + body: Bootstrapper( + stages: [SimpleStage(), SimpleStage2()], + onCompleted: completer, + ), + ), + ); +} diff --git a/lib/src/about_screen.dart b/lib/src/about_screen.dart new file mode 100644 index 0000000..5d3aac2 --- /dev/null +++ b/lib/src/about_screen.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:markettakers/main.dart'; +import 'package:markettakers/src/loaders/loader.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class AboutScreen extends StatelessWidget { + final String decription; + final List children; + const AboutScreen({ + super.key, + required this.decription, + this.children = const [], + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 2, + child: Loader(flavour: LoaderFlavour.big, playing: true), + ), + Flexible( + flex: 1, + child: Text( + decription, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16, color: Colors.grey[500]), + ), + ), + + Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + Credits(), + + Footer(children: children), + const Text("MarketTakers (c) 2025"), + ], + ), + ), + ), + ], + ); + } +} + +class Footer extends StatelessWidget { + const Footer({super.key, required this.children}); + + final List children; + + @override + Widget build(BuildContext context) { + return FractionallySizedBox( + widthFactor: 0.5, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => TalkerScreen(talker: talker), + ), + ); + }, + child: const Text("View Logs"), + ), + ...children, + ], + ), + ); + } +} + +class Credits extends StatelessWidget { + const Credits({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Made with "), + const Icon(Icons.favorite, color: Colors.red), + const Text(" by Skipper, Clewerwild and karabyn2187"), + ], + ); + } +} diff --git a/lib/src/bootstrapper.dart b/lib/src/bootstrapper.dart new file mode 100644 index 0000000..ae705f1 --- /dev/null +++ b/lib/src/bootstrapper.dart @@ -0,0 +1,193 @@ +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/src/loaders/loader.dart'; + +part 'bootstrapper.freezed.dart'; + +abstract class StageFactory { + String get title; + + Future get isCompleted async => false; + + Future start(StageController controller); + void dispose() {} +} + +@freezed +class StageState with _$StageState { + final String title; + final double progress; + + StageState({required this.title, required this.progress}); +} + +class StageController extends Cubit { + StageController({required String title}) + : super(StageState(title: title, progress: 0.0)); + + void updateProgress(double progress) { + emit(state.copyWith(progress: progress)); + } + + void updateTitle(String title) { + emit(state.copyWith(title: title)); + } +} + +typedef Stages = List; + +@freezed +class BootstrapState with _$BootstrapState { + final Stages stages; + final int currentStageIndex; + final StageController? controller; + + StageFactory get currentStage => stages[currentStageIndex]; + bool get isCompleted => currentStageIndex > stages.length; + + BootstrapState(this.stages, {this.currentStageIndex = 0, this.controller}); +} + +@freezed +sealed class BootstrapEvent with _$BootstrapEvent { + const factory BootstrapEvent.start() = StartEvent; + const factory BootstrapEvent.stageCompleted() = StageCompletedEvent; + const factory BootstrapEvent.finished() = FinishedEvent; +} + +class BootstrapController extends Bloc { + BootstrapController(super.initialState) { + assert(state.stages.isNotEmpty, "Stages list cannot be empty"); + + on((event, emit) { + talker.info("BootstrapController: Starting bootstrap process"); + final controller = launchCurrentStage( + state.stages, + state.currentStageIndex, + ); + emit(state.copyWith(controller: controller)); + }); + on((event, emit) async { + talker.info("BootstrapController: ${state.currentStage.title} completed"); + state.currentStage.dispose(); + + final nextIndex = state.currentStageIndex + 1; + final newState = state.copyWith(currentStageIndex: nextIndex); + + if (newState.isCompleted) { + talker.info("BootstrapController: All stages completed"); + } else if (await newState.currentStage.isCompleted) { + talker.info( + "BootstrapController: Stage ${newState.currentStage.title} already completed, skipping", + ); + add(const BootstrapEvent.stageCompleted()); + } else { + final nextStage = newState.currentStage; + talker.info("BootstrapController: Starting stage ${nextStage.title}"); + launchCurrentStage(state.stages, nextIndex); + emit(newState); + } + }); + } + + 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); + }), + ); + + return controller; + } +} + +class BootstrapFooter extends StatelessWidget { + const BootstrapFooter({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final controller = state.controller; + if (controller == null) { + return const SizedBox.shrink(); + } + return Column( + children: [ + Text( + "${state.currentStage.title} ${controller.state.progress.toStringAsFixed(2)}%", + ), + CircularProgressIndicator.adaptive( + value: controller.state.progress, + ), + ], + ); + }, + ); + } +} + +class Bootstrapper extends StatelessWidget { + final Stages stages; + final Completer onCompleted; + + const Bootstrapper({ + super.key, + required this.stages, + required this.onCompleted, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) { + final controller = BootstrapController(BootstrapState(stages)); + controller.add(const BootstrapEvent.start()); + controller.launchCurrentStage(stages, 0); + return controller; + }, + child: BlocListener( + listener: (context, state) { + if (state.isCompleted) { + onCompleted.complete(); + } + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + flex: 3, + child: Center( + child: Loader(flavour: LoaderFlavour.big, playing: true), + ), + ), + Flexible(flex: 1, child: BootstrapFooter()), + ], + ), + ), + ); + } +} diff --git a/lib/src/bootstrapper.freezed.dart b/lib/src/bootstrapper.freezed.dart new file mode 100644 index 0000000..2c65038 --- /dev/null +++ b/lib/src/bootstrapper.freezed.dart @@ -0,0 +1,649 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'bootstrapper.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$StageState { + + String get title; double get progress; +/// Create a copy of StageState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$StageStateCopyWith get copyWith => _$StageStateCopyWithImpl(this as StageState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is StageState&&(identical(other.title, title) || other.title == title)&&(identical(other.progress, progress) || other.progress == progress)); +} + + +@override +int get hashCode => Object.hash(runtimeType,title,progress); + +@override +String toString() { + return 'StageState(title: $title, progress: $progress)'; +} + + +} + +/// @nodoc +abstract mixin class $StageStateCopyWith<$Res> { + factory $StageStateCopyWith(StageState value, $Res Function(StageState) _then) = _$StageStateCopyWithImpl; +@useResult +$Res call({ + String title, double progress +}); + + + + +} +/// @nodoc +class _$StageStateCopyWithImpl<$Res> + implements $StageStateCopyWith<$Res> { + _$StageStateCopyWithImpl(this._self, this._then); + + final StageState _self; + final $Res Function(StageState) _then; + +/// Create a copy of StageState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? progress = null,}) { + return _then(StageState( +title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String,progress: null == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable +as double, + )); +} + +} + + +/// Adds pattern-matching-related methods to [StageState]. +extension StageStatePatterns on StageState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(){ +final _that = this; +switch (_that) { +case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(){ +final _that = this; +switch (_that) { +case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({required TResult orElse(),}) {final _that = this; +switch (_that) { +case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when() {final _that = this; +switch (_that) { +case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull() {final _that = this; +switch (_that) { +case _: + return null; + +} +} + +} + +/// @nodoc +mixin _$BootstrapState { + + Stages get stages; int get currentStageIndex; StageController? get controller; +/// Create a copy of BootstrapState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BootstrapStateCopyWith get copyWith => _$BootstrapStateCopyWithImpl(this as BootstrapState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BootstrapState&&const DeepCollectionEquality().equals(other.stages, stages)&&(identical(other.currentStageIndex, currentStageIndex) || other.currentStageIndex == currentStageIndex)&&(identical(other.controller, controller) || other.controller == controller)); +} + + +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(stages),currentStageIndex,controller); + +@override +String toString() { + return 'BootstrapState(stages: $stages, currentStageIndex: $currentStageIndex, controller: $controller)'; +} + + +} + +/// @nodoc +abstract mixin class $BootstrapStateCopyWith<$Res> { + factory $BootstrapStateCopyWith(BootstrapState value, $Res Function(BootstrapState) _then) = _$BootstrapStateCopyWithImpl; +@useResult +$Res call({ + List stages, int currentStageIndex, StageController? controller +}); + + + + +} +/// @nodoc +class _$BootstrapStateCopyWithImpl<$Res> + implements $BootstrapStateCopyWith<$Res> { + _$BootstrapStateCopyWithImpl(this._self, this._then); + + final BootstrapState _self; + final $Res Function(BootstrapState) _then; + +/// Create a copy of BootstrapState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? stages = null,Object? currentStageIndex = null,Object? controller = freezed,}) { + return _then(BootstrapState( +null == stages ? _self.stages : stages // ignore: cast_nullable_to_non_nullable +as List,currentStageIndex: null == currentStageIndex ? _self.currentStageIndex : currentStageIndex // ignore: cast_nullable_to_non_nullable +as int,controller: freezed == controller ? _self.controller : controller // ignore: cast_nullable_to_non_nullable +as StageController?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [BootstrapState]. +extension BootstrapStatePatterns on BootstrapState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(){ +final _that = this; +switch (_that) { +case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(){ +final _that = this; +switch (_that) { +case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({required TResult orElse(),}) {final _that = this; +switch (_that) { +case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when() {final _that = this; +switch (_that) { +case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull() {final _that = this; +switch (_that) { +case _: + return null; + +} +} + +} + +/// @nodoc +mixin _$BootstrapEvent { + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BootstrapEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'BootstrapEvent()'; +} + + +} + +/// @nodoc +class $BootstrapEventCopyWith<$Res> { +$BootstrapEventCopyWith(BootstrapEvent _, $Res Function(BootstrapEvent) __); +} + + +/// Adds pattern-matching-related methods to [BootstrapEvent]. +extension BootstrapEventPatterns on BootstrapEvent { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({TResult Function( StartEvent value)? start,TResult Function( StageCompletedEvent value)? stageCompleted,TResult Function( FinishedEvent value)? finished,required TResult orElse(),}){ +final _that = this; +switch (_that) { +case StartEvent() when start != null: +return start(_that);case StageCompletedEvent() when stageCompleted != null: +return stageCompleted(_that);case FinishedEvent() when finished != null: +return finished(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map({required TResult Function( StartEvent value) start,required TResult Function( StageCompletedEvent value) stageCompleted,required TResult Function( FinishedEvent value) finished,}){ +final _that = this; +switch (_that) { +case StartEvent(): +return start(_that);case StageCompletedEvent(): +return stageCompleted(_that);case FinishedEvent(): +return finished(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull({TResult? Function( StartEvent value)? start,TResult? Function( StageCompletedEvent value)? stageCompleted,TResult? Function( FinishedEvent value)? finished,}){ +final _that = this; +switch (_that) { +case StartEvent() when start != null: +return start(_that);case StageCompletedEvent() when stageCompleted != null: +return stageCompleted(_that);case FinishedEvent() when finished != null: +return finished(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({TResult Function()? start,TResult Function()? stageCompleted,TResult Function()? finished,required TResult orElse(),}) {final _that = this; +switch (_that) { +case StartEvent() when start != null: +return start();case StageCompletedEvent() when stageCompleted != null: +return stageCompleted();case FinishedEvent() when finished != null: +return finished();case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when({required TResult Function() start,required TResult Function() stageCompleted,required TResult Function() finished,}) {final _that = this; +switch (_that) { +case StartEvent(): +return start();case StageCompletedEvent(): +return stageCompleted();case FinishedEvent(): +return finished();} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull({TResult? Function()? start,TResult? Function()? stageCompleted,TResult? Function()? finished,}) {final _that = this; +switch (_that) { +case StartEvent() when start != null: +return start();case StageCompletedEvent() when stageCompleted != null: +return stageCompleted();case FinishedEvent() when finished != null: +return finished();case _: + return null; + +} +} + +} + +/// @nodoc + + +class StartEvent implements BootstrapEvent { + const StartEvent(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is StartEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'BootstrapEvent.start()'; +} + + +} + + + + +/// @nodoc + + +class StageCompletedEvent implements BootstrapEvent { + const StageCompletedEvent(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is StageCompletedEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'BootstrapEvent.stageCompleted()'; +} + + +} + + + + +/// @nodoc + + +class FinishedEvent implements BootstrapEvent { + const FinishedEvent(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is FinishedEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'BootstrapEvent.finished()'; +} + + +} + + + + +// dart format on diff --git a/lib/src/error_screen.dart b/lib/src/error_screen.dart new file mode 100644 index 0000000..a82b14e --- /dev/null +++ b/lib/src/error_screen.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class ErrorScreen extends StatelessWidget { + final Object error; + + const ErrorScreen({super.key, required this.error}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + flex: 2, + child: const Icon(Icons.error, color: Colors.red, size: 200), + ), + Flexible( + flex: 2, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text.rich( + TextSpan( + text: 'Ooops... error! ', + style: Theme.of(context).textTheme.displaySmall, + ), + ), + const SizedBox.square(dimension: 10), + const Icon(Icons.sentiment_very_dissatisfied), + ], + ), + ), + Flexible( + flex: 1, + child: Text( + error.toString(), + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + ], + ); + } +} diff --git a/lib/src/loaders/base_loader.dart b/lib/src/loaders/base_loader.dart new file mode 100644 index 0000000..d26f8c8 --- /dev/null +++ b/lib/src/loaders/base_loader.dart @@ -0,0 +1,67 @@ +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); + } + }, + ); + } +} diff --git a/lib/src/loaders/base_loader.g.dart b/lib/src/loaders/base_loader.g.dart new file mode 100644 index 0000000..4412774 --- /dev/null +++ b/lib/src/loaders/base_loader.g.dart @@ -0,0 +1,62 @@ +// 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 { + 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(value), + ); + } +} + +String _$loaderStateHash() => r'e220e72e138a376390817c6e5a4051c7e2c665b3'; + +abstract class _$LoaderState extends $Notifier { + bool build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + bool, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/src/loaders/loader.dart b/lib/src/loaders/loader.dart new file mode 100644 index 0000000..4173551 --- /dev/null +++ b/lib/src/loaders/loader.dart @@ -0,0 +1,19 @@ +import 'package:markettakers/src/loaders/base_loader.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'); + + const LoaderFlavour({required this.filepath}); + + final String filepath; +} + +class Loader extends BaseLoader { + final LoaderFlavour flavour; + final bool playing; + + Loader({super.key, required this.flavour, this.playing = false}) + : super(filepath: flavour.filepath, fit: Fit.contain); +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..5eb11ea --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,42 @@ +name: markettakers +description: "MarketTakers library" +version: 1.0.0 +publish_to: none + +environment: + sdk: ^3.9.2 + flutter: ">=1.17.0" + +dependencies: + bloc: ^9.0.1 + flutter: + sdk: flutter + flutter_bloc: ^9.1.1 + flutter_hooks: ^0.21.3+1 + freezed_annotation: ^3.1.0 + hooks_riverpod: ^3.0.2 + rive: ^0.14.0-dev.9 + rive_native: ^0.0.12 + riverpod: ^3.0.2 + riverpod_annotation: ^3.0.2 + talker_flutter: ^5.0.2 + +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 + + + +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + assets: + - assets/animations/big_loaders/ + - assets/animations/loaders/