commit 49965fc3fcee70870743561683594b36460559a4 Author: hdbg Date: Thu Oct 9 17:50:04 2025 +0200 misc: initial commit 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 0000000..e5696db Binary files /dev/null and b/assets/animations/big_loaders/big_logo_splash.riv differ diff --git a/assets/animations/loaders/small_loader.riv b/assets/animations/loaders/small_loader.riv new file mode 100644 index 0000000..85bd590 Binary files /dev/null and b/assets/animations/loaders/small_loader.riv differ 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/