[0.1.0]
This commit is contained in:
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 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
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
|
|
||||||
|
# FVM Version Cache
|
||||||
|
.fvm/
|
||||||
33
.metadata
Normal file
33
.metadata
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# 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: "bae5e49bc2a867403c43b2aae2de8f8c33b037e4"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
- platform: android
|
||||||
|
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
- platform: ios
|
||||||
|
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"dart.flutterSdkPath": ".fvm/versions/stable"
|
||||||
|
}
|
||||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!---
|
||||||
|
## Guidelines for changelog/git
|
||||||
|
**INIT**: Initial commit in a repo, only committed once.
|
||||||
|
**DOC**: Updates/Refactors in the documents.
|
||||||
|
**CHORE**: Minor fixes and/or architecture enhancements.
|
||||||
|
**DEPS**: Dependency updates and the resulting refactors.
|
||||||
|
**SYNC**: Regular sync commits to ensure perfect syncing of all the development devices (Describes the feature in the working).
|
||||||
|
**TEST**: Adding/Updating test files.
|
||||||
|
**FIX**: Fixing a bug/issue that prevents a functionality.
|
||||||
|
**FEAT**: Add a completely new functionality or extend an existing one in a non-breaking manner.
|
||||||
|
**BREAKING CHANGE**: A [FEAT] but with breaking changes (Is only proposed as FEAT in the git commit).
|
||||||
|
## Version Composition
|
||||||
|
x.y.z
|
||||||
|
* Update z on Deps and Fixes.
|
||||||
|
* Update y on Feat that are large enough.
|
||||||
|
* Update x on BREAKING CHANGE that are a must and to signal phases completed [Alpha - Beta - Stable].
|
||||||
|
-->
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
- **INIT**: Initial commit in the new repo.
|
||||||
1
COPYRIGHT
Normal file
1
COPYRIGHT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This repository is Developed, Maintained, and is property of Michael W. Aziz (Micazi).
|
||||||
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
## Astromic: Helpers
|
||||||
|
|
||||||
|
The helpers module of the **Astromic** Presentation System
|
||||||
|
Developed, Maintained, and is property of Michael W. Aziz (Micazi)
|
||||||
|
|
||||||
|
### Content
|
||||||
|
|
||||||
|
- Form Helper ☑️
|
||||||
|
- Loading Helper ☑️
|
||||||
|
- Sheet Helper ☑️
|
||||||
|
- Presenting Helper ☑️
|
||||||
|
- Dialog Helper
|
||||||
|
- Listing Helper
|
||||||
|
- SnackBar Helper
|
||||||
47
analysis_options.yaml
Normal file
47
analysis_options.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
# ==== Project Organization Rules ====
|
||||||
|
# Enforces relative imports to maintain project structure and avoid unnecessary long paths
|
||||||
|
- prefer_relative_imports
|
||||||
|
|
||||||
|
# Ensures that files are named in lowercase_with_underscores format
|
||||||
|
- file_names
|
||||||
|
|
||||||
|
# ==== Best Practices ====
|
||||||
|
# Enforces the closing of streams and sinks to avoid memory leaks
|
||||||
|
- close_sinks
|
||||||
|
|
||||||
|
# Avoids empty 'else' blocks to reduce confusion and improve code readability
|
||||||
|
- avoid_empty_else
|
||||||
|
|
||||||
|
# Prefer using 'const' constructors wherever possible for better performance and immutability
|
||||||
|
- prefer_const_constructors
|
||||||
|
|
||||||
|
# Avoid leading underscores for local variable names to prevent conflicts and improve clarity
|
||||||
|
- no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
|
# ==== Code Consistency ====
|
||||||
|
# Avoids the use of 'print' statements in production code, encouraging proper logging instead
|
||||||
|
- avoid_print
|
||||||
|
|
||||||
|
# Encourages using 'final' for fields that are not reassigned to promote immutability
|
||||||
|
- prefer_final_fields
|
||||||
|
|
||||||
|
# Ensures that all types are explicitly specified for better readability and type safety
|
||||||
|
- always_specify_types
|
||||||
|
|
||||||
|
# Avoids redundant default argument values to keep the code clean
|
||||||
|
- avoid_redundant_argument_values
|
||||||
|
|
||||||
|
# Enforces consistency by preferring single quotes over double quotes for string literals
|
||||||
|
- prefer_single_quotes
|
||||||
|
|
||||||
|
# ==== Documentation Rules ====
|
||||||
|
# Enforces documentation for all public classes, methods, and fields to improve API clarity
|
||||||
|
# - public_member_api_docs
|
||||||
|
|
||||||
|
# ==== Null Safety ====
|
||||||
|
# Avoids unnecessary null checks and encourages the use of null-aware operators
|
||||||
|
- unnecessary_null_checks
|
||||||
6
lib/astromic_helpers.dart
Normal file
6
lib/astromic_helpers.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
library astromic_helpers;
|
||||||
|
|
||||||
|
export 'src/form/form_helper.astromic.dart';
|
||||||
|
export 'src/loading/loading_helper.astromic.dart';
|
||||||
|
export 'src/sheet/sheet_helper.astromic.dart';
|
||||||
|
export 'src/presenting/presenting_helper.astromic.dart';
|
||||||
34
lib/dependencies/vsync-provider/vsync_provider.dart
Normal file
34
lib/dependencies/vsync-provider/vsync_provider.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class VsyncProvider extends StatefulWidget {
|
||||||
|
//SECTION - Widget Arguments
|
||||||
|
final Widget child;
|
||||||
|
//!SECTION
|
||||||
|
//
|
||||||
|
const VsyncProvider({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
//--
|
||||||
|
|
||||||
|
static VsyncProviderState of(BuildContext context) {
|
||||||
|
final VsyncProviderState? result = context.findAncestorStateOfType<VsyncProviderState>();
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw FlutterError('No VsyncProvider ancestor found in the widget tree!');
|
||||||
|
}
|
||||||
|
|
||||||
|
//--
|
||||||
|
@override
|
||||||
|
State<VsyncProvider> createState() => VsyncProviderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class VsyncProviderState extends State<VsyncProvider> with TickerProviderStateMixin {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//SECTION - Build Return
|
||||||
|
return widget.child;
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
}
|
||||||
3
lib/src/form/form_helper.astromic.dart
Normal file
3
lib/src/form/form_helper.astromic.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export 'package:form_controller/form_controller.dart';
|
||||||
|
export 'src/controller.dart';
|
||||||
|
export 'src/form_field.dart';
|
||||||
146
lib/src/form/src/controller.dart
Normal file
146
lib/src/form/src/controller.dart
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
import 'package:form_controller/form_controller.dart';
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models & Widgets
|
||||||
|
import 'enums/enums.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
/// A specialized form controller to handle form states,
|
||||||
|
class AstromicFormController extends FormController {
|
||||||
|
// State Variables
|
||||||
|
final Map<String, AstromicFieldState> fieldStates = <String, AstromicFieldState>{};
|
||||||
|
final Map<String, String?> fieldMessages = <String, String?>{};
|
||||||
|
final Map<String, dynamic> _hostedValues = <String, dynamic>{};
|
||||||
|
final Map<String, Map<String, String>>? streamedErrorMaps; // fieldId: {errorCode: errorMessage}
|
||||||
|
final Stream<List<String>>? errorStream;
|
||||||
|
// State Stream Variables
|
||||||
|
static final StreamController<(String, AstromicFieldState)> _stateStreamController = StreamController<(String id, AstromicFieldState)>.broadcast();
|
||||||
|
final Stream<(String id, AstromicFieldState)> stateStream = _stateStreamController.stream;
|
||||||
|
|
||||||
|
AstromicFormController({
|
||||||
|
Map<String, (String initialText, bool initialObscurity)>? extraControllers,
|
||||||
|
this.streamedErrorMaps,
|
||||||
|
this.errorStream,
|
||||||
|
}) : super(controllers: extraControllers) {
|
||||||
|
// Add states and messages based on initial controller.
|
||||||
|
_addInitialControllers();
|
||||||
|
|
||||||
|
// Listen on the error stream for values and push to the corresponding field state.
|
||||||
|
if (errorStream != null) {
|
||||||
|
_handleErrorStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the field state and message of a specific field using it's ID.
|
||||||
|
(AstromicFieldState, String? message)? getState(String fieldId) {
|
||||||
|
if (fieldStates.containsKey(fieldId)) {
|
||||||
|
return (fieldStates[fieldId]!, fieldMessages[fieldId]);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the field state and message of a specific field using it's ID.
|
||||||
|
void setState(String fieldId, AstromicFieldState state, {String? message}) {
|
||||||
|
if (!fieldStates.containsKey(fieldId)) {
|
||||||
|
throw FlutterError('Field ID $fieldId does not exist.');
|
||||||
|
}
|
||||||
|
fieldStates[fieldId] = state;
|
||||||
|
fieldMessages[fieldId] = message;
|
||||||
|
_stateStreamController.add((fieldId, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the state of a specific field using it's ID.
|
||||||
|
resetState(String fieldId) {
|
||||||
|
setState(fieldId, AstromicFieldState.idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the value of a hosted state variable using it's ID.
|
||||||
|
void setValue<T>(String id, T data) {
|
||||||
|
if (_hostedValues.keys.toList().contains(id)) {
|
||||||
|
_hostedValues[id] = data;
|
||||||
|
} else {
|
||||||
|
_hostedValues.addEntries(<MapEntry<String, dynamic>>[MapEntry<String, dynamic>(id, data)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value of a hosted state variable using it's ID.
|
||||||
|
T? getValue<T>(String id) {
|
||||||
|
if (_hostedValues.keys.toList().contains(id)) {
|
||||||
|
if (_hostedValues[id] is T) {
|
||||||
|
return _hostedValues[id];
|
||||||
|
} else {
|
||||||
|
throw FlutterError('Value found but is not of the type $T');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextEditingController controller(String id, {String? initialText, bool isObscure = false}) {
|
||||||
|
if (super.controllers == null || !super.controllers!.keys.toList().contains(id)) {
|
||||||
|
fieldStates.addEntries(<MapEntry<String, AstromicFieldState>>[MapEntry<String, AstromicFieldState>(id, AstromicFieldState.idle)]);
|
||||||
|
fieldMessages.addEntries(<MapEntry<String, String?>>[MapEntry<String, String?>(id, null)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditingController ret = super.controller(id, initialText: initialText, isObscure: isObscure);
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||||
|
if (ret.text.isEmpty) {
|
||||||
|
ret.text = initialText ?? '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> dispose() async {
|
||||||
|
_stateStreamController.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
//SECTION - Helper Methods
|
||||||
|
_addInitialControllers() {
|
||||||
|
// Add in the initial field states...
|
||||||
|
fieldStates.addEntries(super.controllers?.entries.toList().map((MapEntry<String, (String, bool)> e) => MapEntry<String, AstromicFieldState>(
|
||||||
|
e.key, // controller ID
|
||||||
|
AstromicFieldState.idle, // Initial state of any new controller is Idle
|
||||||
|
)) ??
|
||||||
|
<MapEntry<String, AstromicFieldState>>{});
|
||||||
|
|
||||||
|
// Add in the initial field messages...
|
||||||
|
fieldMessages.addEntries(super.controllers?.entries.toList().map((MapEntry<String, (String, bool)> e) => MapEntry<String, String?>(
|
||||||
|
e.key, // Controller ID
|
||||||
|
null, // The initial message it has which is Null
|
||||||
|
)) ??
|
||||||
|
<MapEntry<String, String?>>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleErrorStream() {
|
||||||
|
errorStream!.distinct().listen((List<String> errorCodes) {
|
||||||
|
if (streamedErrorMaps != null && streamedErrorMaps!.isNotEmpty) {
|
||||||
|
for (String errorMapId in streamedErrorMaps!.keys.toList()) {
|
||||||
|
if (super.controllers != null && super.controllers!.containsKey(errorMapId)) {
|
||||||
|
if (streamedErrorMaps![errorMapId] != null &&
|
||||||
|
streamedErrorMaps![errorMapId]!.isNotEmpty &&
|
||||||
|
streamedErrorMaps![errorMapId]!.keys.toList().where((String k) => errorCodes.contains(k)).toList().isNotEmpty) {
|
||||||
|
for (String eC in streamedErrorMaps![errorMapId]!.keys.toList().where((String k) => errorCodes.contains(k)).toList()) {
|
||||||
|
String? m = streamedErrorMaps![errorMapId]![eC];
|
||||||
|
setState(errorMapId, AstromicFieldState.withError, message: m ?? 'Error Message was not set!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
1
lib/src/form/src/enums/enums.exports.dart
Normal file
1
lib/src/form/src/enums/enums.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'state.enum.dart';
|
||||||
30
lib/src/form/src/enums/state.enum.dart
Normal file
30
lib/src/form/src/enums/state.enum.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
enum AstromicFieldState {
|
||||||
|
idle,
|
||||||
|
withInfo,
|
||||||
|
withWarning,
|
||||||
|
withError,
|
||||||
|
withSuccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FieldStateExtension on AstromicFieldState {
|
||||||
|
T valuefyer<T>(
|
||||||
|
T idle, {
|
||||||
|
T? withInfo,
|
||||||
|
T? withWarning,
|
||||||
|
T? withError,
|
||||||
|
T? withSuccess,
|
||||||
|
}) {
|
||||||
|
switch (this) {
|
||||||
|
case AstromicFieldState.idle:
|
||||||
|
return idle;
|
||||||
|
case AstromicFieldState.withInfo:
|
||||||
|
return withInfo ?? idle;
|
||||||
|
case AstromicFieldState.withWarning:
|
||||||
|
return withWarning ?? idle;
|
||||||
|
case AstromicFieldState.withError:
|
||||||
|
return withError ?? idle;
|
||||||
|
case AstromicFieldState.withSuccess:
|
||||||
|
return withSuccess ?? idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
315
lib/src/form/src/form_field.dart
Normal file
315
lib/src/form/src/form_field.dart
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
import 'package:astromic_elements/astromic_elements.dart';
|
||||||
|
import 'package:form_controller/form_controller.dart';
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models & Widgets
|
||||||
|
import 'controller.dart';
|
||||||
|
import 'enums/enums.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class AstromicFormField<T> extends StatefulWidget {
|
||||||
|
//SECTION - Widget Arguments
|
||||||
|
//S1 -- Shared
|
||||||
|
final AstromicFormController formController;
|
||||||
|
final String formID;
|
||||||
|
final bool? initialObscurity;
|
||||||
|
//
|
||||||
|
final AstromicFieldConfiguration? configuration;
|
||||||
|
final List<FormControllerValidator>? validators;
|
||||||
|
final bool? resetMessageOnChange;
|
||||||
|
//
|
||||||
|
final AstromicFieldStyle Function(bool isEnabled, bool isFocused, AstromicFieldState state)? style;
|
||||||
|
//
|
||||||
|
final String? hint;
|
||||||
|
final Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter, AstromicFieldState state)? prefixWidget;
|
||||||
|
final Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter, AstromicFieldState state)? suffixWidget;
|
||||||
|
final Widget? Function(bool isEnabled, bool isFocused, AstromicFieldState state, String? message)? messageBuilder;
|
||||||
|
//S1 -- Text Specific
|
||||||
|
String? initialText;
|
||||||
|
void Function(String v, AstromicFieldState state)? onChanged;
|
||||||
|
void Function(String v, AstromicFieldState state)? onSubmited;
|
||||||
|
Iterable<ContextMenuButtonItem>? contextButtons;
|
||||||
|
//S1 -- Action Specific
|
||||||
|
(T item, String label)? initialValue;
|
||||||
|
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onTap;
|
||||||
|
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onHold;
|
||||||
|
String Function(String? oldValue, String newValue)? onValueChangedMapper;
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
AstromicFormField.text({
|
||||||
|
required this.formController,
|
||||||
|
required this.formID,
|
||||||
|
this.initialObscurity,
|
||||||
|
this.configuration,
|
||||||
|
this.validators,
|
||||||
|
this.resetMessageOnChange,
|
||||||
|
this.style,
|
||||||
|
this.hint,
|
||||||
|
this.prefixWidget,
|
||||||
|
this.suffixWidget,
|
||||||
|
this.messageBuilder,
|
||||||
|
//
|
||||||
|
this.initialText,
|
||||||
|
this.onChanged,
|
||||||
|
this.onSubmited,
|
||||||
|
this.contextButtons,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicFormField.action({
|
||||||
|
required this.formController,
|
||||||
|
required this.formID,
|
||||||
|
this.initialObscurity,
|
||||||
|
this.configuration,
|
||||||
|
this.validators,
|
||||||
|
this.resetMessageOnChange,
|
||||||
|
this.style,
|
||||||
|
this.hint,
|
||||||
|
this.prefixWidget,
|
||||||
|
this.suffixWidget,
|
||||||
|
this.messageBuilder,
|
||||||
|
//
|
||||||
|
this.initialValue,
|
||||||
|
this.onTap,
|
||||||
|
this.onHold,
|
||||||
|
this.onValueChangedMapper,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AstromicFormField<T>> createState() => _AstromicFormFieldState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AstromicFormFieldState<T> extends State<AstromicFormField<T>> {
|
||||||
|
//
|
||||||
|
//SECTION - State Variables
|
||||||
|
//s1 --State
|
||||||
|
late AstromicFieldState _currentState;
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers
|
||||||
|
late TextEditingController _controller;
|
||||||
|
//s1 --Controllers
|
||||||
|
//
|
||||||
|
//s1 --Constants
|
||||||
|
//s1 --Constants
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
_currentState = AstromicFieldState.idle;
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
// Set the textEditingController for this field
|
||||||
|
_controller = widget.formController.controller(widget.formID, initialText: widget.initialText, isObscure: widget.initialObscurity ?? false);
|
||||||
|
|
||||||
|
// Listen to the state stream for updated form states...
|
||||||
|
widget.formController.stateStream.listen(((String, AstromicFieldState) newState) {
|
||||||
|
if (mounted && widget.formID == newState.$1) {
|
||||||
|
setState(() {
|
||||||
|
_currentState = newState.$2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen to the error stream for updated errors and set the field's state accordingly...
|
||||||
|
if (widget.formController.errorStream != null) {
|
||||||
|
widget.formController.errorStream!.listen((List<String> errorCodes) {
|
||||||
|
if (widget.formController.streamedErrorMaps != null) {
|
||||||
|
// fieldId: {errorCode: errorMessage}
|
||||||
|
for (String cID in widget.formController.streamedErrorMaps!.keys.toList()) {
|
||||||
|
if (cID == widget.formID && widget.formController.streamedErrorMaps![cID] != null) {
|
||||||
|
for (MapEntry<String, String> errMapItem in widget.formController.streamedErrorMaps![cID]!.entries) {
|
||||||
|
if (errorCodes.map((String x) => x.toString()).contains(errMapItem.key.toString())) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_setFieldErrorState(widget.formID, errMapItem.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
//SECTION - Dumb Widgets
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Stateless functions
|
||||||
|
_setFieldErrorState(String id, String? message) {
|
||||||
|
widget.formController.setState(id, AstromicFieldState.withError, message: message);
|
||||||
|
}
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Action Callbacks
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//SECTION - Build Setup
|
||||||
|
//s1 --Values
|
||||||
|
//double w = MediaQuery.of(context).size.width;
|
||||||
|
//double h = MediaQuery.of(context).size.height;
|
||||||
|
//s1 --Values
|
||||||
|
//
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
Widget buildText() {
|
||||||
|
return AstromicFields.text(
|
||||||
|
stateKey: widget.formController.fieldStateKey(widget.formID),
|
||||||
|
controller: _controller,
|
||||||
|
onChanged: (String s) {
|
||||||
|
setState(() {
|
||||||
|
if (widget.resetMessageOnChange ?? false) {
|
||||||
|
widget.formController.resetState(widget.formID);
|
||||||
|
}
|
||||||
|
if (widget.onChanged != null) {
|
||||||
|
widget.onChanged!(s, _currentState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSubmited: (String s) {
|
||||||
|
if (widget.onSubmited != null) {
|
||||||
|
widget.onSubmited!(s, _currentState);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
configuration: widget.configuration?.copyWith(
|
||||||
|
isTextObscured: widget.formController.isObscure(widget.formID),
|
||||||
|
validator: (widget.validators != null && widget.validators!.isNotEmpty)
|
||||||
|
? (bool enabled, bool focused, String? s) {
|
||||||
|
widget.formController.resetState(widget.formID);
|
||||||
|
//
|
||||||
|
List<FormControllerValidator> validators = <FormControllerValidator>[];
|
||||||
|
//
|
||||||
|
validators.addAll(widget.validators!);
|
||||||
|
//
|
||||||
|
Map<FormControllerValidator, bool> checks = <FormControllerValidator, bool>{};
|
||||||
|
//
|
||||||
|
for (FormControllerValidator validator in validators) {
|
||||||
|
bool res = validator.checker(s);
|
||||||
|
checks.addEntries(<MapEntry<FormControllerValidator, bool>>[MapEntry<FormControllerValidator, bool>(validator, res)]);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (checks.containsValue(false)) {
|
||||||
|
// It has an Error!
|
||||||
|
_setFieldErrorState(widget.formID, checks.entries.where((MapEntry<FormControllerValidator, bool> e) => e.value == false).toList().first.key.message);
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
// It has no Errors!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
style: (bool enabled, bool focused) => widget.style != null ? widget.style!(enabled, focused, _currentState) : const AstromicFieldStyle(),
|
||||||
|
//
|
||||||
|
contextButtons: widget.contextButtons,
|
||||||
|
//
|
||||||
|
hint: widget.hint,
|
||||||
|
prefixWidget: widget.prefixWidget != null ? (bool enabled, bool focused, void Function() setter) => widget.prefixWidget!(enabled, focused, setter, _currentState) : null,
|
||||||
|
suffixWidget: widget.suffixWidget != null ? (bool enabled, bool focused, void Function() setter) => widget.suffixWidget!(enabled, focused, setter, _currentState) : null,
|
||||||
|
messageBuilder:
|
||||||
|
widget.messageBuilder != null ? (bool enabled, bool focused) => widget.messageBuilder!(enabled, focused, _currentState, widget.formController.getState(widget.formID)?.$2) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildAction() {
|
||||||
|
return AstromicFields.action(
|
||||||
|
stateKey: widget.formController.fieldStateKey(widget.formID),
|
||||||
|
controller: _controller,
|
||||||
|
initialValue: widget.initialValue,
|
||||||
|
onTap: widget.onTap,
|
||||||
|
onHold: widget.onHold,
|
||||||
|
onValueChangedMapper: widget.onValueChangedMapper!,
|
||||||
|
//
|
||||||
|
style: (bool enabled) => widget.style != null ? widget.style!(enabled, false, _currentState) : const AstromicFieldStyle(),
|
||||||
|
configuration: widget.configuration?.copyWith(
|
||||||
|
isTextObscured: widget.formController.isObscure(widget.formID),
|
||||||
|
validator: (widget.validators != null && widget.validators!.isNotEmpty)
|
||||||
|
? (bool enabled, bool focused, String? s) {
|
||||||
|
widget.formController.resetState(widget.formID);
|
||||||
|
//
|
||||||
|
List<FormControllerValidator> validators = <FormControllerValidator>[];
|
||||||
|
//
|
||||||
|
validators.addAll(widget.validators!);
|
||||||
|
//
|
||||||
|
Map<FormControllerValidator, bool> checks = <FormControllerValidator, bool>{};
|
||||||
|
//
|
||||||
|
for (FormControllerValidator validator in validators) {
|
||||||
|
bool res = validator.checker(s);
|
||||||
|
checks.addEntries(<MapEntry<FormControllerValidator, bool>>[MapEntry<FormControllerValidator, bool>(validator, res)]);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (checks.containsValue(false)) {
|
||||||
|
// It has an Error!
|
||||||
|
_setFieldErrorState(widget.formID, checks.entries.where((MapEntry<FormControllerValidator, bool> e) => e.value == false).toList().first.key.message);
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
// It has no Errors!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
//
|
||||||
|
hint: widget.hint,
|
||||||
|
prefixWidget: widget.prefixWidget != null ? (bool enabled, void Function() setter) => widget.prefixWidget!(enabled, false, setter, _currentState) : null,
|
||||||
|
suffixWidget: widget.suffixWidget != null ? (bool enabled, void Function() setter) => widget.suffixWidget!(enabled, false, setter, _currentState) : null,
|
||||||
|
messageBuilder: widget.messageBuilder != null ? (bool enabled) => widget.messageBuilder!(enabled, false, _currentState, widget.formController.getState(widget.formID)?.$2) : null,
|
||||||
|
//
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Build Return
|
||||||
|
return widget.onValueChangedMapper != null
|
||||||
|
?
|
||||||
|
// Is Action Field
|
||||||
|
buildAction()
|
||||||
|
:
|
||||||
|
// Is Text Field
|
||||||
|
buildText();
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
//SECTION - Disposable variables
|
||||||
|
_controller.dispose();
|
||||||
|
//!SECTION
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
84
lib/src/loading/loading_helper.astromic.dart
Normal file
84
lib/src/loading/loading_helper.astromic.dart
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
import 'package:loader_overlay/loader_overlay.dart';
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models
|
||||||
|
import 'src/models/models.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicLoadingHelper {
|
||||||
|
static AstromicLoadingOverlayStyle _style = AstromicLoadingOverlayStyle(loadingWidget: (_) => Container());
|
||||||
|
|
||||||
|
/// Initialize the global builder method
|
||||||
|
static Widget initialize({
|
||||||
|
required Widget child,
|
||||||
|
required AstromicLoadingOverlayStyle style,
|
||||||
|
}) {
|
||||||
|
_style = style;
|
||||||
|
return GlobalLoaderOverlay(
|
||||||
|
child: child,
|
||||||
|
disableBackButton: !style.canDismess,
|
||||||
|
closeOnBackButton: style.canDismess,
|
||||||
|
//
|
||||||
|
duration: style.inDuration,
|
||||||
|
reverseDuration: style.outDuration,
|
||||||
|
switchInCurve: style.inCurve,
|
||||||
|
switchOutCurve: style.outCurve,
|
||||||
|
transitionBuilder: (Widget w, Animation<double> a) => style.animationBuilder?.call(w, a),
|
||||||
|
//
|
||||||
|
overlayColor: Colors.transparent,
|
||||||
|
overlayWidgetBuilder: (dynamic progress) => style.loadingWidget(progress),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current visiblity state of the loader.
|
||||||
|
static bool isShowing(BuildContext context) => context.loaderOverlay.visible;
|
||||||
|
|
||||||
|
/// Show the loader with an optional progress parameter.
|
||||||
|
static Future<void> start(BuildContext context, {dynamic startProgress}) async {
|
||||||
|
context.loaderOverlay.show(progress: startProgress);
|
||||||
|
return await Future<dynamic>.delayed(_style.inDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hide the loader.
|
||||||
|
static Future<void> stop(BuildContext context, {dynamic startProgress}) async {
|
||||||
|
context.loaderOverlay.hide();
|
||||||
|
return await Future<dynamic>.delayed(_style.outDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the progres in the builder.
|
||||||
|
static Future<void> updateProgress(BuildContext context, dynamic progress) async {
|
||||||
|
context.loaderOverlay.progress(progress);
|
||||||
|
return await Future<dynamic>.delayed(Duration(milliseconds: (_style.inDuration.inMilliseconds / 2).floor()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quickly add in a loading segment for an Async function.
|
||||||
|
static Future<T?> load<T>(
|
||||||
|
BuildContext context,
|
||||||
|
Future<T?> Function(Future<void> Function(dynamic p) updateProgress) process, {
|
||||||
|
dynamic startingProgress,
|
||||||
|
Function(dynamic e, StackTrace trace)? onFail,
|
||||||
|
}) async {
|
||||||
|
Completer<T> c = Completer<T>();
|
||||||
|
//
|
||||||
|
await start(context, startProgress: startingProgress);
|
||||||
|
try {
|
||||||
|
T? p = await process((dynamic p) async {
|
||||||
|
return await updateProgress(context, p);
|
||||||
|
});
|
||||||
|
c.complete(p);
|
||||||
|
} catch (e, trace) {
|
||||||
|
c.complete(null);
|
||||||
|
if (onFail != null) onFail(e, trace);
|
||||||
|
} finally {
|
||||||
|
await stop(context);
|
||||||
|
}
|
||||||
|
return c.future;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
lib/src/loading/src/models/models.exports.dart
Normal file
1
lib/src/loading/src/models/models.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'style.model.dart';
|
||||||
56
lib/src/loading/src/models/style.model.dart
Normal file
56
lib/src/loading/src/models/style.model.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AstromicLoadingOverlayStyle {
|
||||||
|
final Widget Function(dynamic progress) loadingWidget;
|
||||||
|
//
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final (int sigmaX, int sigmaY)? backgroundBlur;
|
||||||
|
//
|
||||||
|
final bool canDismess;
|
||||||
|
//
|
||||||
|
final Duration inDuration;
|
||||||
|
final Duration outDuration;
|
||||||
|
final Curve inCurve;
|
||||||
|
final Curve outCurve;
|
||||||
|
final Function(Widget child, Animation<double> animation)? animationBuilder;
|
||||||
|
AstromicLoadingOverlayStyle({
|
||||||
|
required this.loadingWidget,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.backgroundBlur,
|
||||||
|
this.canDismess = false,
|
||||||
|
this.inCurve = Curves.easeOut,
|
||||||
|
this.inDuration = const Duration(milliseconds: 250),
|
||||||
|
this.outCurve = Curves.easeIn,
|
||||||
|
this.outDuration = const Duration(milliseconds: 250),
|
||||||
|
this.animationBuilder,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicLoadingOverlayStyle copyWith({
|
||||||
|
Widget Function(dynamic progress)? loadingWidget,
|
||||||
|
Color? backgroundColor,
|
||||||
|
(int sigmaX, int sigmaY)? backgroundBlur,
|
||||||
|
bool? canDismess,
|
||||||
|
Duration? inDuration,
|
||||||
|
Duration? outDuration,
|
||||||
|
Curve? inCurve,
|
||||||
|
Curve? outCurve,
|
||||||
|
Function(Widget child, Animation<double> animation)? animationBuilder,
|
||||||
|
}) {
|
||||||
|
return AstromicLoadingOverlayStyle(
|
||||||
|
loadingWidget: loadingWidget ?? this.loadingWidget,
|
||||||
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
|
backgroundBlur: backgroundBlur ?? this.backgroundBlur,
|
||||||
|
canDismess: canDismess ?? this.canDismess,
|
||||||
|
inDuration: inDuration ?? this.inDuration,
|
||||||
|
outDuration: outDuration ?? this.outDuration,
|
||||||
|
inCurve: inCurve ?? this.inCurve,
|
||||||
|
outCurve: outCurve ?? this.outCurve,
|
||||||
|
animationBuilder: animationBuilder ?? this.animationBuilder,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AstromicLoadingOverlayStyle(loadingWidget: $loadingWidget, backgroundColor: $backgroundColor, backgroundBlur: $backgroundBlur, canDismess: $canDismess, inDuration: $inDuration, outDuration: $outDuration, inCurve: $inCurve, outCurve: $outCurve, animationBuilder: $animationBuilder)';
|
||||||
|
}
|
||||||
|
}
|
||||||
1
lib/src/loading/src/src.exports.dart
Normal file
1
lib/src/loading/src/src.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'models/models.exports.dart';
|
||||||
3
lib/src/presenting/presenting_helper.astromic.dart
Normal file
3
lib/src/presenting/presenting_helper.astromic.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export './src/enums/enums.exports.dart';
|
||||||
|
export './src/models/models.exports.dart';
|
||||||
|
export './src/widgets/widgets.exports.dart';
|
||||||
1
lib/src/presenting/src/enums/enums.exports.dart
Normal file
1
lib/src/presenting/src/enums/enums.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export './presenter_state.enum.dart';
|
||||||
9
lib/src/presenting/src/enums/presenter_state.enum.dart
Normal file
9
lib/src/presenting/src/enums/presenter_state.enum.dart
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
enum AstromicPresenterState {
|
||||||
|
initialLoad, // The first load in the presenter.
|
||||||
|
//
|
||||||
|
loading, // all subsequent loadings to the presenter
|
||||||
|
loaded, // Loaded the data
|
||||||
|
//
|
||||||
|
empty, // Data is empty (for list types)
|
||||||
|
error, // Errors arrising in the presenter
|
||||||
|
}
|
||||||
1
lib/src/presenting/src/helpers/helpers.exports.dart
Normal file
1
lib/src/presenting/src/helpers/helpers.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export './snapshot_helper.dart';
|
||||||
92
lib/src/presenting/src/helpers/snapshot_helper.dart
Normal file
92
lib/src/presenting/src/helpers/snapshot_helper.dart
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import '../enums/presenter_state.enum.dart';
|
||||||
|
import '../models/presenter_configuration.model.dart';
|
||||||
|
import '../models/presenter_return.model.dart';
|
||||||
|
|
||||||
|
class SnapshotHelper {
|
||||||
|
/// Get the state of the snapshot & it's return model.
|
||||||
|
static (AstromicPresenterState state, PresenterReturnModel<T>? returnModel) stateGetter<T>(
|
||||||
|
AsyncSnapshot<T> snapshot, T? lastFetchedData, dynamic lastError, AstromicPresenterConfiguration configurations, int? oldBatchId, int currentBatchId) {
|
||||||
|
ConnectionState state = snapshot.connectionState;
|
||||||
|
T? data = snapshot.data;
|
||||||
|
T? previousData = lastFetchedData;
|
||||||
|
dynamic error = snapshot.error;
|
||||||
|
dynamic previousError = lastError;
|
||||||
|
//
|
||||||
|
bool hasError = error != null && snapshot.hasError;
|
||||||
|
bool hasPreviousError = previousError != null;
|
||||||
|
//
|
||||||
|
bool hasData = data != null && snapshot.hasData;
|
||||||
|
bool hasPreviousData = previousData != null;
|
||||||
|
//
|
||||||
|
bool isDataEmpty = hasData && ((data is List && data.isEmpty) || (data is Map && data.isEmpty));
|
||||||
|
bool isPreviousDataEmpty = hasPreviousData && ((previousData is List && previousData.isEmpty) || (previousData is Map && previousData.isEmpty));
|
||||||
|
//
|
||||||
|
bool disbaleLoadingWithData = configurations.disableLoadingWithPrevData;
|
||||||
|
bool disbaleLoadingWithError = configurations.disableLoadingWithPrevError;
|
||||||
|
//
|
||||||
|
AstromicPresenterState rState;
|
||||||
|
PresenterReturnModel<T>? rModel;
|
||||||
|
|
||||||
|
if (state == ConnectionState.waiting) {
|
||||||
|
//S1 -- Is Wating/Loading
|
||||||
|
if (hasPreviousData) {
|
||||||
|
//S2 -- I have previous data while it's loading
|
||||||
|
if (!disbaleLoadingWithData) {
|
||||||
|
//S3 -- Show the loading state even if i have data!
|
||||||
|
rState = AstromicPresenterState.loading;
|
||||||
|
rModel = null;
|
||||||
|
} else {
|
||||||
|
//S3 -- hide the loading state when i have previous data!
|
||||||
|
if (isPreviousDataEmpty) {
|
||||||
|
//S4 -- Previous data is empty and i'm not showing the loading state, show the last empty state!
|
||||||
|
rState = AstromicPresenterState.empty;
|
||||||
|
rModel = PresenterSuccessReturnModel<T>(oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
} else {
|
||||||
|
//S4 -- Previous data is NOT empty and i'm not showing the loading state, show the last data state!
|
||||||
|
rState = AstromicPresenterState.loaded;
|
||||||
|
rModel = PresenterSuccessReturnModel<T>(data: previousData, oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//S2 -- I DON'T have previous data while it's loading (either first load or had an error.)
|
||||||
|
if (hasPreviousError) {
|
||||||
|
//S3 -- The previous snap was errored
|
||||||
|
if (!disbaleLoadingWithError) {
|
||||||
|
//S4 -- The previous snap was errored, but i will show loading.
|
||||||
|
rState = AstromicPresenterState.loading;
|
||||||
|
rModel = null;
|
||||||
|
} else {
|
||||||
|
//S4 -- The previous snap was errored, i will keep it.
|
||||||
|
rState = AstromicPresenterState.error;
|
||||||
|
rModel = PresenterFailureReturnModel<T>(error: previousError, oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//S3 -- The previous snap was NOT errored, So it's the initial load!
|
||||||
|
rState = AstromicPresenterState.initialLoad;
|
||||||
|
rModel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//S1 -- Is NOT Waiting/Loading
|
||||||
|
if (hasData && !hasError) {
|
||||||
|
//S2 -- Done loading and i have data!
|
||||||
|
if (isDataEmpty) {
|
||||||
|
//S3 -- The loaded data is Empty
|
||||||
|
rState = AstromicPresenterState.empty;
|
||||||
|
rModel = PresenterSuccessReturnModel<T>(initialData: previousData, oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
} else {
|
||||||
|
//S3 -- The loaded data is NOT Empty
|
||||||
|
rState = AstromicPresenterState.loaded;
|
||||||
|
rModel = PresenterSuccessReturnModel<T>(data: data, initialData: previousData, oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//S2 -- Done loading but there is NO data! (errored)
|
||||||
|
rState = AstromicPresenterState.error;
|
||||||
|
rModel = PresenterFailureReturnModel<T>(error: error, initialData: previousData, oldBatchId: oldBatchId, batchId: currentBatchId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
return (rState, rModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
lib/src/presenting/src/models/models.exports.dart
Normal file
2
lib/src/presenting/src/models/models.exports.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export './presenter_return.model.dart';
|
||||||
|
export './presenter_configuration.model.dart';
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class AstromicPresenterConfiguration {
|
||||||
|
final Duration? timeoutDuration;
|
||||||
|
final bool disableLoadingWithPrevData;
|
||||||
|
final bool disableLoadingWithPrevError;
|
||||||
|
AstromicPresenterConfiguration({
|
||||||
|
this.timeoutDuration,
|
||||||
|
this.disableLoadingWithPrevData = false,
|
||||||
|
this.disableLoadingWithPrevError = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicPresenterConfiguration copyWith({
|
||||||
|
int? initialCycle,
|
||||||
|
Duration? timeoutDuration,
|
||||||
|
bool? disableLoadingWithPrevData,
|
||||||
|
bool? disableLoadingWithPrevError,
|
||||||
|
VoidCallback? stateSetter,
|
||||||
|
}) {
|
||||||
|
return AstromicPresenterConfiguration(
|
||||||
|
timeoutDuration: timeoutDuration ?? this.timeoutDuration,
|
||||||
|
disableLoadingWithPrevData: disableLoadingWithPrevData ?? this.disableLoadingWithPrevData,
|
||||||
|
disableLoadingWithPrevError: disableLoadingWithPrevError ?? this.disableLoadingWithPrevError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/src/presenting/src/models/presenter_return.model.dart
Normal file
22
lib/src/presenting/src/models/presenter_return.model.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
class PresenterReturnModel<T> {
|
||||||
|
final int? oldBatchId; // The batch of the old data
|
||||||
|
final int batchId; // The batch of the new data
|
||||||
|
final T? initialData; // The old data if exists
|
||||||
|
final T? data; // New data if exists
|
||||||
|
final dynamic error; // Errors if exists
|
||||||
|
PresenterReturnModel({
|
||||||
|
required this.batchId,
|
||||||
|
this.oldBatchId,
|
||||||
|
this.error,
|
||||||
|
this.data,
|
||||||
|
this.initialData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PresenterSuccessReturnModel<T> extends PresenterReturnModel<T> {
|
||||||
|
PresenterSuccessReturnModel({super.initialData, super.data, super.error, required super.batchId, super.oldBatchId});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PresenterFailureReturnModel<T> extends PresenterReturnModel<T> {
|
||||||
|
PresenterFailureReturnModel({super.initialData, super.data, super.error, required super.batchId, super.oldBatchId});
|
||||||
|
}
|
||||||
179
lib/src/presenting/src/widgets/future_presenter.widget.dart
Normal file
179
lib/src/presenting/src/widgets/future_presenter.widget.dart
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models & Widgets
|
||||||
|
import './presenter_controller.widget.dart';
|
||||||
|
import '../enums/enums.exports.dart';
|
||||||
|
import '../models/models.exports.dart';
|
||||||
|
import '../helpers/helpers.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicFuturePresenter<T> extends StatefulWidget {
|
||||||
|
//SECTION - Widget Arguments
|
||||||
|
final AstromicPresenterController futureController;
|
||||||
|
final String futureId;
|
||||||
|
//
|
||||||
|
final Map<AstromicPresenterState, Widget Function(PresenterReturnModel<T?>? r)> stateBuilder;
|
||||||
|
final AstromicPresenterConfiguration? configuration;
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
const AstromicFuturePresenter({
|
||||||
|
super.key,
|
||||||
|
required this.futureController,
|
||||||
|
required this.futureId,
|
||||||
|
required this.stateBuilder,
|
||||||
|
this.configuration,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AstromicFuturePresenter<T>> createState() => _AstromicFuturePresenterState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AstromicFuturePresenterState<T> extends State<AstromicFuturePresenter<T>> {
|
||||||
|
//SECTION - State Variables
|
||||||
|
//s1 --State
|
||||||
|
Future<T?>? _future;
|
||||||
|
late AstromicPresenterConfiguration _configs;
|
||||||
|
//
|
||||||
|
T? previousData;
|
||||||
|
dynamic previousError;
|
||||||
|
int? previousBatchId;
|
||||||
|
late int _refreshKey; // Key to force Future uniqueness on refresh
|
||||||
|
//s1 --State
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
_configs = widget.configuration ?? AstromicPresenterConfiguration();
|
||||||
|
_refreshKey = 0;
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
widget.futureController.getRefreshStream(widget.futureId).listen((_) {
|
||||||
|
_refreshFuture(); // Force future recreation on refresh
|
||||||
|
});
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
_initializeFuture();
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant AstromicFuturePresenter<T> oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
//
|
||||||
|
if (oldWidget.futureId != widget.futureId) {
|
||||||
|
_initializeFuture();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
//SECTION - Dumb Widgets
|
||||||
|
final Map<AstromicPresenterState, Widget> _defaultstateBuilders = {
|
||||||
|
AstromicPresenterState.initialLoad: const Center(child: CircularProgressIndicator()),
|
||||||
|
AstromicPresenterState.loading: const Center(child: CircularProgressIndicator()),
|
||||||
|
AstromicPresenterState.empty: const Center(child: Text('No data at the moment!')),
|
||||||
|
AstromicPresenterState.error: const Center(child: Text('an error has occured, check the log!')),
|
||||||
|
AstromicPresenterState.loaded: const Center(child: Text('I have the data, replace this with a widget!')),
|
||||||
|
};
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Stateless functions
|
||||||
|
//S1 -- Set Data
|
||||||
|
void _setPrevious(T? data, int batchId, dynamic error) async {
|
||||||
|
//
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
|
previousData = data ?? previousData;
|
||||||
|
previousBatchId = data != null ? batchId : previousBatchId;
|
||||||
|
previousError = error ?? previousError;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//S1 -- Method to reinitialize or update `_future` with a new instance
|
||||||
|
void _refreshFuture() {
|
||||||
|
// Increment the refresh key to ensure a unique future instance
|
||||||
|
_refreshKey++;
|
||||||
|
setState(() {
|
||||||
|
_initializeFuture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//S1 -- Method to reinitialize or update `_future` with a new instance
|
||||||
|
void _initializeFuture() {
|
||||||
|
_future = widget.futureController.getFuture(widget.futureId)?.then((result) => result) as Future<T?>?;
|
||||||
|
}
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Action Callbacks
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//SECTION - Build Setup
|
||||||
|
//s1 --Values
|
||||||
|
//double w = MediaQuery.of(context).size.width;
|
||||||
|
//double h = MediaQuery.of(context).size.height;
|
||||||
|
//s1 --Values
|
||||||
|
//
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Build Return
|
||||||
|
return FutureBuilder<T?>(
|
||||||
|
key: ValueKey(_refreshKey), // This ensures FutureBuilder re-runs
|
||||||
|
future: _future,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
int? oldBatchId = previousBatchId;
|
||||||
|
int currentBatchId = snapshot.hashCode;
|
||||||
|
|
||||||
|
(AstromicPresenterState state, PresenterReturnModel<T?>? returnModel) ret = SnapshotHelper.stateGetter<T?>(snapshot, previousData, previousError, _configs, oldBatchId, currentBatchId);
|
||||||
|
AstromicPresenterState returnedState = ret.$1;
|
||||||
|
PresenterReturnModel<T?>? returnedModel = ret.$2;
|
||||||
|
Widget defaultBuilder = _defaultstateBuilders[returnedState]!;
|
||||||
|
|
||||||
|
if (returnedState == AstromicPresenterState.loaded || returnedState == AstromicPresenterState.empty || returnedState == AstromicPresenterState.error) {
|
||||||
|
_setPrevious(returnedModel?.data, currentBatchId, returnedModel?.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return widget.stateBuilder.containsKey(returnedState) ? widget.stateBuilder[returnedState]?.call(returnedModel) ?? defaultBuilder : defaultBuilder;
|
||||||
|
});
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() async {
|
||||||
|
super.dispose();
|
||||||
|
//SECTION - Disposable variables
|
||||||
|
await widget.futureController.disposeFuture(widget.futureId);
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
/// A contrller used to control Futures/Streams to present them effeciantly.
|
||||||
|
class AstromicPresenterController {
|
||||||
|
late final Map<String, (Future Function()? fetch, int c)> _futures;
|
||||||
|
late final Map<String, Stream?> _streams;
|
||||||
|
|
||||||
|
late final Map<String, StreamController<void>> _futureRefreshers;
|
||||||
|
|
||||||
|
AstromicPresenterController({
|
||||||
|
Map<String, (Future Function()? fetch, int startCycle)> futures = const {},
|
||||||
|
Map<String, Stream?> streams = const {},
|
||||||
|
}) : _futures = futures.map((k, v) => MapEntry(k, (v.$1, v.$2))),
|
||||||
|
_futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController<void>.broadcast())),
|
||||||
|
_streams = streams;
|
||||||
|
|
||||||
|
/// Get the current cycle of this future ID.
|
||||||
|
int getFutureCycle(String id) {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
return _futures[id]!.$2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the future using it's ID.
|
||||||
|
Future<T?>? getFuture<T>(String id) {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
|
||||||
|
return _futures[id]!.$1!() as Future<T?>?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the stream using it's ID.
|
||||||
|
Stream<T?>? getStream<T>(String id) {
|
||||||
|
assert(_streams.containsKey(id), 'did you add a stream with this id?');
|
||||||
|
|
||||||
|
return _streams[id]! as Stream<T?>?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the refresh stream of a future using it's ID.
|
||||||
|
Stream<void> getRefreshStream<T>(String id) {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
|
||||||
|
return _futureRefreshers[id]!.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the fetching cycle of a future using it's ID.
|
||||||
|
void setFutureCycle(String id, int cycle) {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
|
||||||
|
_futures[id] = (_futures[id]!.$1, cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refresh a future using it's ID.
|
||||||
|
void refreshFuture(String id) {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
|
||||||
|
_futureRefreshers[id]!.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispose of a future using it's ID.
|
||||||
|
Future<void> disposeFuture(String id) async {
|
||||||
|
assert(_futures.containsKey(id), 'did you add a future with this id?');
|
||||||
|
|
||||||
|
return await _futureRefreshers[id]!.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
166
lib/src/presenting/src/widgets/stream_presenter.widget.dart
Normal file
166
lib/src/presenting/src/widgets/stream_presenter.widget.dart
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models
|
||||||
|
import '../enums/enums.exports.dart';
|
||||||
|
import '../models/models.exports.dart';
|
||||||
|
import '../helpers/helpers.exports.dart';
|
||||||
|
//s3 Widgets
|
||||||
|
import './presenter_controller.widget.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicStreamPresenter<T> extends StatefulWidget {
|
||||||
|
//SECTION - Widget Arguments
|
||||||
|
final AstromicPresenterController controller;
|
||||||
|
final String streamId;
|
||||||
|
//
|
||||||
|
final Map<AstromicPresenterState, Widget Function(PresenterReturnModel<T?>? r)> stateBuilder;
|
||||||
|
final AstromicPresenterConfiguration? configuration;
|
||||||
|
//!SECTION
|
||||||
|
//
|
||||||
|
const AstromicStreamPresenter({
|
||||||
|
super.key,
|
||||||
|
required this.controller,
|
||||||
|
required this.streamId,
|
||||||
|
required this.stateBuilder,
|
||||||
|
this.configuration,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AstromicStreamPresenter<T>> createState() => _AstromicStreamPresenterState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AstromicStreamPresenterState<T> extends State<AstromicStreamPresenter<T>> {
|
||||||
|
//
|
||||||
|
//SECTION - State Variables
|
||||||
|
//s1 --State
|
||||||
|
Stream<T?>? _stream;
|
||||||
|
late AstromicPresenterConfiguration _configs;
|
||||||
|
//
|
||||||
|
T? previousData;
|
||||||
|
dynamic previousError;
|
||||||
|
int? previousBatchId;
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers
|
||||||
|
//late AstromicFormController _formController;
|
||||||
|
//s1 --Controllers
|
||||||
|
//
|
||||||
|
//s1 --Constants
|
||||||
|
//s1 --Constants
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
_configs = widget.configuration ?? AstromicPresenterConfiguration();
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
_initializeStream();
|
||||||
|
//s1 --Late & Async Initializers
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant AstromicStreamPresenter<T> oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
//
|
||||||
|
if (oldWidget.streamId != widget.streamId) {
|
||||||
|
_initializeStream();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --State
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
//SECTION - Dumb Widgets
|
||||||
|
final Map<AstromicPresenterState, Widget> _defaultstateBuilders = <AstromicPresenterState, Widget>{
|
||||||
|
AstromicPresenterState.initialLoad: const Center(child: CircularProgressIndicator()),
|
||||||
|
AstromicPresenterState.loading: const Center(child: CircularProgressIndicator()),
|
||||||
|
AstromicPresenterState.empty: const Center(child: Text('No data at the moment!')),
|
||||||
|
AstromicPresenterState.error: const Center(child: Text('an error has occured, check the log!')),
|
||||||
|
AstromicPresenterState.loaded: const Center(child: Text('I have the data, replace this with a widget!')),
|
||||||
|
};
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Stateless functions
|
||||||
|
//S1 -- Set Data
|
||||||
|
void _setPrevious(T? data, int batchId, dynamic error) async {
|
||||||
|
//
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
|
previousData = data ?? previousData;
|
||||||
|
previousBatchId = data != null ? batchId : previousBatchId;
|
||||||
|
previousError = error ?? previousError;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//S1 -- Method to reinitialize or update `_future` with a new instance
|
||||||
|
void _initializeStream() {
|
||||||
|
_stream = widget.controller.getStream(widget.streamId)?.asyncMap((dynamic result) => result) as Stream<T?>?;
|
||||||
|
}
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Action Callbacks
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//SECTION - Build Setup
|
||||||
|
//s1 --Values
|
||||||
|
//double w = MediaQuery.of(context).size.width;
|
||||||
|
//double h = MediaQuery.of(context).size.height;
|
||||||
|
//s1 --Values
|
||||||
|
//
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
//s1 --Contexted Widgets
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Build Return
|
||||||
|
return StreamBuilder<T?>(
|
||||||
|
stream: _stream,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<T?> snapshot) {
|
||||||
|
//
|
||||||
|
int? oldBatchId = previousBatchId;
|
||||||
|
int currentBatchId = snapshot.hashCode;
|
||||||
|
//
|
||||||
|
(AstromicPresenterState state, PresenterReturnModel<T?>? returnModel) ret = SnapshotHelper.stateGetter<T?>(snapshot, previousData, previousError, _configs, oldBatchId, currentBatchId);
|
||||||
|
AstromicPresenterState returnedState = ret.$1;
|
||||||
|
PresenterReturnModel<T?>? returnedModel = ret.$2;
|
||||||
|
Widget defaultBuilder = _defaultstateBuilders[returnedState]!;
|
||||||
|
//
|
||||||
|
if (returnedState == AstromicPresenterState.loaded || returnedState == AstromicPresenterState.empty || returnedState == AstromicPresenterState.error) {
|
||||||
|
_setPrevious(returnedModel?.data, currentBatchId, returnedModel?.error);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
return widget.stateBuilder.containsKey(returnedState) ? widget.stateBuilder[returnedState]?.call(returnedModel) ?? defaultBuilder : defaultBuilder;
|
||||||
|
//
|
||||||
|
});
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
}
|
||||||
3
lib/src/presenting/src/widgets/widgets.exports.dart
Normal file
3
lib/src/presenting/src/widgets/widgets.exports.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export 'presenter_controller.widget.dart';
|
||||||
|
export 'future_presenter.widget.dart';
|
||||||
|
export 'stream_presenter.widget.dart';
|
||||||
219
lib/src/sheet/sheet_helper.astromic.dart
Normal file
219
lib/src/sheet/sheet_helper.astromic.dart
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
import '../../dependencies/vsync-provider/vsync_provider.dart';
|
||||||
|
//s3 Models
|
||||||
|
import '../form/form_helper.astromic.dart';
|
||||||
|
import '../sheet/src/models/models.exports.dart';
|
||||||
|
import '../sheet/src/enums/enums.exports.dart';
|
||||||
|
import '../sheet/src/widgets/widgets.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
export '../sheet/src/models/models.exports.dart';
|
||||||
|
export '../form/form_helper.astromic.dart' show AstromicFormController;
|
||||||
|
|
||||||
|
class AstromicSheetHelper {
|
||||||
|
static VsyncProviderState? _vsyncState;
|
||||||
|
|
||||||
|
/// A wrapper to initialize the vSync plugin for the sheets' animations.
|
||||||
|
static Widget vSyncBuilder(Widget child) {
|
||||||
|
return VsyncProvider(child: child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An initializer method to initialize the vSync state.
|
||||||
|
static _initializevSync(BuildContext context) {
|
||||||
|
_vsyncState = VsyncProvider.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show FLEX sheet, a flexible sheet that hugs the height of it's content.
|
||||||
|
static Future<T?> flex<T extends Object?>(
|
||||||
|
BuildContext context, {
|
||||||
|
//
|
||||||
|
Widget? headSection,
|
||||||
|
required Widget contentSection,
|
||||||
|
Widget? footerSection,
|
||||||
|
//
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
//
|
||||||
|
}) async {
|
||||||
|
if (_vsyncState == null) {
|
||||||
|
_initializevSync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstromicSheetConfiguration sheetConfigs = configuration ?? const AstromicSheetConfiguration();
|
||||||
|
AstromicSheetStyle sheetStyle = style ?? const AstromicSheetStyle();
|
||||||
|
|
||||||
|
return await BasicSheet.show<T>(
|
||||||
|
context: context,
|
||||||
|
vsyncState: _vsyncState!,
|
||||||
|
//
|
||||||
|
useRootNavigator: sheetConfigs.useRootNavigator,
|
||||||
|
enableOutsideInteraction: sheetConfigs.enableOutsideInteraction,
|
||||||
|
enableSheetInteraction: sheetConfigs.enableSheetInteraction,
|
||||||
|
//
|
||||||
|
forwardAnimationDuration: sheetConfigs.forwardAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
reverseAnimationDuration: sheetConfigs.reverseAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
animationCurve: sheetConfigs.animationCurve ?? Curves.easeOut,
|
||||||
|
//
|
||||||
|
barrierColor: sheetStyle.maskColor ,
|
||||||
|
radius: sheetStyle.radius,
|
||||||
|
//
|
||||||
|
child: BaseSheetWidget<T?>(
|
||||||
|
sheetConfiguration: sheetConfigs,
|
||||||
|
sheetStyle: sheetStyle,
|
||||||
|
//
|
||||||
|
headSection: headSection,
|
||||||
|
contentSection: contentSection,
|
||||||
|
footerSection: footerSection,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show FLEX sheet using a pre-set FlexSheetTemplate.
|
||||||
|
static Future<T?> flexTemplate<T extends Object?>(BuildContext context, AstromicFlexSheetTemplate<T> template) async => await flex<T>(
|
||||||
|
context,
|
||||||
|
headSection: template.headSection,
|
||||||
|
contentSection: template.contentSection,
|
||||||
|
footerSection: template.footerSection,
|
||||||
|
//
|
||||||
|
configuration: template.configuration,
|
||||||
|
style: template.style,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Show Form sheet, a FLEX sheet but with an integrated form controller.
|
||||||
|
static Future<T?> form<T extends Object?>(
|
||||||
|
BuildContext context, {
|
||||||
|
//
|
||||||
|
Widget Function(AstromicFormController)? headSectionBuilder,
|
||||||
|
required Widget Function(AstromicFormController) contentSectionBuilder,
|
||||||
|
Widget Function(AstromicFormController)? footerSectionBuilder,
|
||||||
|
//
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
}) async {
|
||||||
|
if (_vsyncState == null) {
|
||||||
|
_initializevSync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstromicSheetConfiguration sheetConfigs = configuration ?? const AstromicSheetConfiguration();
|
||||||
|
AstromicSheetStyle sheetStyle = style ?? const AstromicSheetStyle();
|
||||||
|
|
||||||
|
return await BasicSheet.show<T>(
|
||||||
|
context: context,
|
||||||
|
vsyncState: _vsyncState!,
|
||||||
|
//
|
||||||
|
useRootNavigator: sheetConfigs.useRootNavigator,
|
||||||
|
enableOutsideInteraction: sheetConfigs.enableOutsideInteraction,
|
||||||
|
enableSheetInteraction: sheetConfigs.enableSheetInteraction,
|
||||||
|
//
|
||||||
|
forwardAnimationDuration: sheetConfigs.forwardAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
reverseAnimationDuration: sheetConfigs.reverseAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
animationCurve: sheetConfigs.animationCurve ?? Curves.easeOut,
|
||||||
|
//
|
||||||
|
barrierColor: sheetStyle.maskColor,
|
||||||
|
radius: sheetStyle.radius,
|
||||||
|
//
|
||||||
|
child: BaseSheetWidget<T?>(
|
||||||
|
sheetType: SheetType.form,
|
||||||
|
//
|
||||||
|
sheetConfiguration: sheetConfigs,
|
||||||
|
sheetStyle: sheetStyle,
|
||||||
|
//
|
||||||
|
headSectionFormBuilder: headSectionBuilder,
|
||||||
|
contentSectionFormBuilder: contentSectionBuilder,
|
||||||
|
footerSectionFormBuilder: footerSectionBuilder,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show Form sheet using a pre-set FormSheetTemplate.
|
||||||
|
static Future<T?> formTemplate<T extends Object?>(BuildContext c, AstromicFormSheetTemplate<T> template) async => await form<T>(
|
||||||
|
c,
|
||||||
|
headSectionBuilder: template.headSectionBuilder,
|
||||||
|
contentSectionBuilder: template.contentSectionBuilder,
|
||||||
|
footerSectionBuilder: template.footerSectionBuilder,
|
||||||
|
//
|
||||||
|
configuration: template.configuration,
|
||||||
|
style: template.style,
|
||||||
|
//
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Show Scroller sheet, a sheet that contains a vertically scrollable elements with some tweeks for animation.
|
||||||
|
static Future<T?> scroller<T extends Object?>(
|
||||||
|
BuildContext context, {
|
||||||
|
//
|
||||||
|
Widget Function(ScrollController)? headSectionBuilder,
|
||||||
|
required Widget Function(ScrollController, ScrollPhysics) contentSectionBuilder,
|
||||||
|
Widget Function(ScrollController)? footerSectionBuilder,
|
||||||
|
//
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
}) async {
|
||||||
|
if (_vsyncState == null) {
|
||||||
|
_initializevSync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstromicSheetConfiguration sheetConfigs = configuration ?? const AstromicSheetConfiguration();
|
||||||
|
AstromicSheetStyle sheetStyle = style ?? const AstromicSheetStyle();
|
||||||
|
|
||||||
|
return await ScrollerSheet.show<T>(
|
||||||
|
context: context,
|
||||||
|
vsyncState: _vsyncState!,
|
||||||
|
//
|
||||||
|
useRootNavigator: sheetConfigs.useRootNavigator,
|
||||||
|
enableOutsideInteraction: sheetConfigs.enableOutsideInteraction,
|
||||||
|
enableSheetInteraction: sheetConfigs.enableSheetInteraction,
|
||||||
|
respectKeyboardInset: sheetConfigs.respectKeyboardInset,
|
||||||
|
//
|
||||||
|
forwardAnimationDuration: sheetConfigs.forwardAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
reverseAnimationDuration: sheetConfigs.reverseAnimationDuration ?? const Duration(milliseconds: 250),
|
||||||
|
animationCurve: sheetConfigs.animationCurve ?? Curves.easeOut,
|
||||||
|
dragThreshold: sheetConfigs.dragThreshold ?? 25,
|
||||||
|
//
|
||||||
|
physics: sheetConfigs.physics,
|
||||||
|
stops: sheetConfigs.expandedHeightFactor != null && (sheetConfigs.expandedHeightFactor! > sheetConfigs.initialHeightFactor)
|
||||||
|
? <double?>[sheetConfigs.initialHeightFactor, sheetConfigs.expandedHeightFactor].map((double? s) {
|
||||||
|
double r = sheetConfigs.safeAreaAware ? ((s! * MediaQuery.of(context).size.height) - MediaQuery.of(context).viewPadding.top) : (s! * MediaQuery.of(context).size.height);
|
||||||
|
return r;
|
||||||
|
}).toList()
|
||||||
|
: <double>[
|
||||||
|
sheetConfigs.initialHeightFactor,
|
||||||
|
].map((double s) {
|
||||||
|
double r = sheetConfigs.safeAreaAware ? ((s * MediaQuery.of(context).size.height) - MediaQuery.of(context).viewPadding.top) : (s * MediaQuery.of(context).size.height);
|
||||||
|
return r;
|
||||||
|
}).toList(),
|
||||||
|
// s2 -- Styling
|
||||||
|
barrierColor: sheetStyle.maskColor,
|
||||||
|
topInset: sheetStyle.topInset,
|
||||||
|
// s2 -- Child
|
||||||
|
builder: (BuildContext context, ScrollController scrollController, ScrollPhysics scrollPhysics, int stop) {
|
||||||
|
return BaseSheetWidget<T?>(
|
||||||
|
sheetType: SheetType.scroller,
|
||||||
|
sheetConfiguration: sheetConfigs,
|
||||||
|
sheetStyle: sheetStyle,
|
||||||
|
headSectionScrollerBuilder: headSectionBuilder,
|
||||||
|
contentSectionScrollBuilder: contentSectionBuilder,
|
||||||
|
footerSectionScrollerBuilder: footerSectionBuilder,
|
||||||
|
scrollController: scrollController,
|
||||||
|
scrollPhysics: scrollPhysics,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show Scroller sheet using a pre-set ScrollerSheetTemplate.
|
||||||
|
static Future<T?> scrollerTemplate<T extends Object?>(BuildContext context, AstromicScrollerSheetTemplate<T> template) async => await scroller<T>(
|
||||||
|
context,
|
||||||
|
headSectionBuilder: template.headSectionBuilder,
|
||||||
|
contentSectionBuilder: template.contentSectionBuilder,
|
||||||
|
footerSectionBuilder: template.footerSectionBuilder,
|
||||||
|
//
|
||||||
|
configuration: template.configuration,
|
||||||
|
style: template.style,
|
||||||
|
);
|
||||||
|
}
|
||||||
1
lib/src/sheet/src/enums/enums.exports.dart
Normal file
1
lib/src/sheet/src/enums/enums.exports.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'type.enum.dart';
|
||||||
5
lib/src/sheet/src/enums/type.enum.dart
Normal file
5
lib/src/sheet/src/enums/type.enum.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
enum SheetType {
|
||||||
|
flex,
|
||||||
|
form,
|
||||||
|
scroller,
|
||||||
|
}
|
||||||
45
lib/src/sheet/src/models/Templates/flex_sheet.template.dart
Normal file
45
lib/src/sheet/src/models/Templates/flex_sheet.template.dart
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models & Widgets
|
||||||
|
import 'template_base.dart';
|
||||||
|
import '../models.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicFlexSheetTemplate<T> extends AstromicSheetTemplate<T> {
|
||||||
|
final Widget? headSection;
|
||||||
|
final Widget contentSection;
|
||||||
|
final Widget? footerSection;
|
||||||
|
//
|
||||||
|
final AstromicSheetConfiguration? configuration;
|
||||||
|
final AstromicSheetStyle? style;
|
||||||
|
|
||||||
|
AstromicFlexSheetTemplate({
|
||||||
|
this.headSection,
|
||||||
|
required this.contentSection,
|
||||||
|
this.footerSection,
|
||||||
|
this.configuration,
|
||||||
|
this.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicFlexSheetTemplate<T> copyWith({
|
||||||
|
Widget? headSection,
|
||||||
|
Widget? contentSection,
|
||||||
|
Widget? footerSection,
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
}) {
|
||||||
|
return AstromicFlexSheetTemplate<T>(
|
||||||
|
headSection: headSection ?? this.headSection,
|
||||||
|
contentSection: contentSection ?? this.contentSection,
|
||||||
|
footerSection: footerSection ?? this.footerSection,
|
||||||
|
configuration: configuration ?? this.configuration,
|
||||||
|
style: style ?? this.style,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
lib/src/sheet/src/models/Templates/form_sheet.template.dart
Normal file
46
lib/src/sheet/src/models/Templates/form_sheet.template.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models
|
||||||
|
import 'template_base.dart';
|
||||||
|
import '../models.exports.dart';
|
||||||
|
import '../../../../form/form_helper.astromic.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicFormSheetTemplate<T> extends AstromicSheetTemplate<T> {
|
||||||
|
final Widget Function(AstromicFormController)? headSectionBuilder;
|
||||||
|
final Widget Function(AstromicFormController) contentSectionBuilder;
|
||||||
|
final Widget Function(AstromicFormController)? footerSectionBuilder;
|
||||||
|
//
|
||||||
|
final AstromicSheetConfiguration? configuration;
|
||||||
|
final AstromicSheetStyle? style;
|
||||||
|
|
||||||
|
AstromicFormSheetTemplate({
|
||||||
|
this.headSectionBuilder,
|
||||||
|
required this.contentSectionBuilder,
|
||||||
|
this.footerSectionBuilder,
|
||||||
|
this.configuration,
|
||||||
|
this.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicFormSheetTemplate<T> copyWith({
|
||||||
|
Widget Function(AstromicFormController)? headSectionBuilder,
|
||||||
|
Widget Function(AstromicFormController)? contentSectionBuilder,
|
||||||
|
Widget Function(AstromicFormController)? footerSectionBuilder,
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
}) {
|
||||||
|
return AstromicFormSheetTemplate<T>(
|
||||||
|
headSectionBuilder: headSectionBuilder ?? this.headSectionBuilder,
|
||||||
|
contentSectionBuilder: contentSectionBuilder ?? this.contentSectionBuilder,
|
||||||
|
footerSectionBuilder: footerSectionBuilder ?? this.footerSectionBuilder,
|
||||||
|
configuration: configuration ?? this.configuration,
|
||||||
|
style: style ?? this.style,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models
|
||||||
|
import 'template_base.dart';
|
||||||
|
import '../models.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class AstromicScrollerSheetTemplate<T> extends AstromicSheetTemplate<T> {
|
||||||
|
final Widget Function(ScrollController)? headSectionBuilder;
|
||||||
|
final Widget Function(ScrollController, ScrollPhysics) contentSectionBuilder;
|
||||||
|
final Widget Function(ScrollController)? footerSectionBuilder;
|
||||||
|
//
|
||||||
|
final AstromicSheetConfiguration? configuration;
|
||||||
|
final AstromicSheetStyle? style;
|
||||||
|
|
||||||
|
AstromicScrollerSheetTemplate({
|
||||||
|
this.headSectionBuilder,
|
||||||
|
required this.contentSectionBuilder,
|
||||||
|
this.footerSectionBuilder,
|
||||||
|
this.configuration,
|
||||||
|
this.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicScrollerSheetTemplate<T> copyWith({
|
||||||
|
Widget Function(ScrollController)? headSection,
|
||||||
|
Widget Function(ScrollController, ScrollPhysics)? contentSectionBuilder,
|
||||||
|
Widget Function(ScrollController)? footerSection,
|
||||||
|
AstromicSheetConfiguration? configuration,
|
||||||
|
AstromicSheetStyle? style,
|
||||||
|
}) {
|
||||||
|
return AstromicScrollerSheetTemplate<T>(
|
||||||
|
headSectionBuilder: headSection ?? this.headSectionBuilder,
|
||||||
|
contentSectionBuilder: contentSectionBuilder ?? this.contentSectionBuilder,
|
||||||
|
footerSectionBuilder: footerSection ?? this.footerSectionBuilder,
|
||||||
|
configuration: configuration ?? this.configuration,
|
||||||
|
style: style ?? this.style,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
lib/src/sheet/src/models/Templates/template_base.dart
Normal file
1
lib/src/sheet/src/models/Templates/template_base.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
abstract class AstromicSheetTemplate<T> {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export 'flex_sheet.template.dart';
|
||||||
|
export 'form_sheet.template.dart';
|
||||||
|
export 'scroller_sheet.template.dart';
|
||||||
73
lib/src/sheet/src/models/configuration.model.dart
Normal file
73
lib/src/sheet/src/models/configuration.model.dart
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AstromicSheetConfiguration {
|
||||||
|
//s1 -- Shared
|
||||||
|
final bool useRootNavigator;
|
||||||
|
//
|
||||||
|
final Duration? closeDelay;
|
||||||
|
final Duration? forwardAnimationDuration;
|
||||||
|
final Duration? reverseAnimationDuration;
|
||||||
|
final Curve? animationCurve;
|
||||||
|
//
|
||||||
|
final bool enableOutsideInteraction;
|
||||||
|
final bool enableSheetInteraction;
|
||||||
|
final bool dismissOnBack;
|
||||||
|
//s1 -- Scroller Specific
|
||||||
|
final double initialHeightFactor;
|
||||||
|
final double? expandedHeightFactor;
|
||||||
|
final bool respectKeyboardInset;
|
||||||
|
final bool safeAreaAware;
|
||||||
|
final ScrollPhysics? physics;
|
||||||
|
final double? dragThreshold;
|
||||||
|
|
||||||
|
const AstromicSheetConfiguration({
|
||||||
|
this.useRootNavigator = true,
|
||||||
|
this.closeDelay,
|
||||||
|
this.forwardAnimationDuration = const Duration(milliseconds: 300),
|
||||||
|
this.reverseAnimationDuration = const Duration(milliseconds: 300),
|
||||||
|
this.animationCurve = Curves.ease,
|
||||||
|
this.enableOutsideInteraction = true,
|
||||||
|
this.enableSheetInteraction = true,
|
||||||
|
this.dismissOnBack = false,
|
||||||
|
this.respectKeyboardInset = true,
|
||||||
|
this.safeAreaAware = false,
|
||||||
|
this.expandedHeightFactor,
|
||||||
|
this.initialHeightFactor = 0.5,
|
||||||
|
this.physics,
|
||||||
|
this.dragThreshold,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicSheetConfiguration copyWith({
|
||||||
|
bool? useRootNavigator,
|
||||||
|
Duration? closeDelay,
|
||||||
|
Duration? forwardAnimationDuration,
|
||||||
|
Duration? reverseAnimationDuration,
|
||||||
|
Curve? animationCurve,
|
||||||
|
bool? enableOutsideInteraction,
|
||||||
|
bool? enableSheetInteraction,
|
||||||
|
bool? dismissOnBack,
|
||||||
|
double? initialHeightFactor,
|
||||||
|
double? expandedHeightFactor,
|
||||||
|
bool? respectKeyboardInset,
|
||||||
|
bool? safeAreaAware,
|
||||||
|
ScrollPhysics? physics,
|
||||||
|
double? dragThreshold,
|
||||||
|
}) {
|
||||||
|
return AstromicSheetConfiguration(
|
||||||
|
useRootNavigator: useRootNavigator ?? this.useRootNavigator,
|
||||||
|
closeDelay: closeDelay ?? this.closeDelay,
|
||||||
|
forwardAnimationDuration: forwardAnimationDuration ?? this.forwardAnimationDuration,
|
||||||
|
reverseAnimationDuration: reverseAnimationDuration ?? this.reverseAnimationDuration,
|
||||||
|
animationCurve: animationCurve ?? this.animationCurve,
|
||||||
|
enableOutsideInteraction: enableOutsideInteraction ?? this.enableOutsideInteraction,
|
||||||
|
enableSheetInteraction: enableSheetInteraction ?? this.enableSheetInteraction,
|
||||||
|
dismissOnBack: dismissOnBack ?? this.dismissOnBack,
|
||||||
|
initialHeightFactor: initialHeightFactor ?? this.initialHeightFactor,
|
||||||
|
expandedHeightFactor: expandedHeightFactor ?? this.expandedHeightFactor,
|
||||||
|
respectKeyboardInset: respectKeyboardInset ?? this.respectKeyboardInset,
|
||||||
|
safeAreaAware: safeAreaAware ?? this.safeAreaAware,
|
||||||
|
physics: physics ?? this.physics,
|
||||||
|
dragThreshold: dragThreshold ?? this.dragThreshold,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
lib/src/sheet/src/models/models.exports.dart
Normal file
3
lib/src/sheet/src/models/models.exports.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export 'configuration.model.dart';
|
||||||
|
export 'style.model.dart';
|
||||||
|
export 'templates/templates.exports.dart';
|
||||||
51
lib/src/sheet/src/models/style.model.dart
Normal file
51
lib/src/sheet/src/models/style.model.dart
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AstromicSheetStyle {
|
||||||
|
final Color maskColor;
|
||||||
|
final Color headSectionColor;
|
||||||
|
final Color contentSectionColor;
|
||||||
|
final Color footerSectionColor;
|
||||||
|
//
|
||||||
|
final EdgeInsets headSectionPadding;
|
||||||
|
final EdgeInsets contentSectionPadding;
|
||||||
|
final EdgeInsets footerSectionPadding;
|
||||||
|
//
|
||||||
|
final double radius;
|
||||||
|
final double topInset;
|
||||||
|
//
|
||||||
|
const AstromicSheetStyle({
|
||||||
|
this.maskColor = Colors.black38,
|
||||||
|
this.headSectionColor = Colors.white,
|
||||||
|
this.contentSectionColor = Colors.white,
|
||||||
|
this.footerSectionColor = Colors.white,
|
||||||
|
this.headSectionPadding = const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0),
|
||||||
|
this.contentSectionPadding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 18.0),
|
||||||
|
this.footerSectionPadding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 18.0),
|
||||||
|
this.radius = 24,
|
||||||
|
this.topInset = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
AstromicSheetStyle copyWith({
|
||||||
|
Color? maskColor,
|
||||||
|
Color? headSectionColor,
|
||||||
|
Color? contentSectionColor,
|
||||||
|
Color? footerSectionColor,
|
||||||
|
EdgeInsets? headSectionPadding,
|
||||||
|
EdgeInsets? contentSectionPadding,
|
||||||
|
EdgeInsets? footerSectionPadding,
|
||||||
|
double? radius,
|
||||||
|
double? topInset,
|
||||||
|
}) {
|
||||||
|
return AstromicSheetStyle(
|
||||||
|
maskColor: maskColor ?? this.maskColor,
|
||||||
|
headSectionColor: headSectionColor ?? this.headSectionColor,
|
||||||
|
contentSectionColor: contentSectionColor ?? this.contentSectionColor,
|
||||||
|
footerSectionColor: footerSectionColor ?? this.footerSectionColor,
|
||||||
|
headSectionPadding: headSectionPadding ?? this.headSectionPadding,
|
||||||
|
contentSectionPadding: contentSectionPadding ?? this.contentSectionPadding,
|
||||||
|
footerSectionPadding: footerSectionPadding ?? this.footerSectionPadding,
|
||||||
|
radius: radius ?? this.radius,
|
||||||
|
topInset: topInset ?? this.topInset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
263
lib/src/sheet/src/widgets/base.dart
Normal file
263
lib/src/sheet/src/widgets/base.dart
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
//s3 Models & Widgets
|
||||||
|
import '../../../form/form_helper.astromic.dart';
|
||||||
|
import '../enums/enums.exports.dart';
|
||||||
|
import '../models/models.exports.dart';
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
class BaseSheetWidget<T> extends StatefulWidget {
|
||||||
|
//SECTION - Widget Arguments
|
||||||
|
final SheetType sheetType;
|
||||||
|
//
|
||||||
|
final AstromicSheetConfiguration? sheetConfiguration;
|
||||||
|
final AstromicSheetStyle? sheetStyle;
|
||||||
|
//
|
||||||
|
final Widget? headSection;
|
||||||
|
final Widget? contentSection;
|
||||||
|
final Widget? footerSection;
|
||||||
|
//
|
||||||
|
final Widget Function(AstromicFormController)? headSectionFormBuilder;
|
||||||
|
final Widget Function(AstromicFormController)? contentSectionFormBuilder;
|
||||||
|
final Widget Function(AstromicFormController)? footerSectionFormBuilder;
|
||||||
|
//
|
||||||
|
final ScrollController? scrollController;
|
||||||
|
final ScrollPhysics? scrollPhysics;
|
||||||
|
final Widget Function(ScrollController)? headSectionScrollerBuilder;
|
||||||
|
final Widget Function(ScrollController, ScrollPhysics)? contentSectionScrollBuilder;
|
||||||
|
final Widget Function(ScrollController)? footerSectionScrollerBuilder;
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
const BaseSheetWidget({
|
||||||
|
super.key,
|
||||||
|
this.sheetType = SheetType.flex,
|
||||||
|
this.sheetConfiguration,
|
||||||
|
this.sheetStyle,
|
||||||
|
this.headSection,
|
||||||
|
this.contentSection,
|
||||||
|
this.footerSection,
|
||||||
|
this.contentSectionFormBuilder,
|
||||||
|
this.footerSectionFormBuilder,
|
||||||
|
this.headSectionFormBuilder,
|
||||||
|
this.scrollController,
|
||||||
|
this.scrollPhysics,
|
||||||
|
this.headSectionScrollerBuilder,
|
||||||
|
this.contentSectionScrollBuilder,
|
||||||
|
this.footerSectionScrollerBuilder,
|
||||||
|
//
|
||||||
|
}) : assert(sheetType != SheetType.flex || (sheetType == SheetType.flex && contentSection != null)),
|
||||||
|
assert(sheetType != SheetType.form || (sheetType == SheetType.form && contentSectionFormBuilder != null)),
|
||||||
|
assert(sheetType != SheetType.scroller || (sheetType == SheetType.scroller && contentSectionScrollBuilder != null && scrollController != null && scrollPhysics != null));
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BaseSheetWidget<T>> createState() => _BaseSheetWidgetState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BaseSheetWidgetState<T> extends State<BaseSheetWidget<T>> {
|
||||||
|
//
|
||||||
|
//SECTION - State Variables
|
||||||
|
//s1 --Controllers
|
||||||
|
late AstromicFormController _formController;
|
||||||
|
late AstromicSheetConfiguration _config;
|
||||||
|
late AstromicSheetStyle _style;
|
||||||
|
//s1 --Controllers
|
||||||
|
//
|
||||||
|
//s1 --State
|
||||||
|
//s1 --State
|
||||||
|
//
|
||||||
|
//s1 --Constants
|
||||||
|
//s1 --Constants
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
//
|
||||||
|
//SECTION - State Variables initializations & Listeners
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
_formController = AstromicFormController();
|
||||||
|
//s1 --Controllers & Listeners
|
||||||
|
//
|
||||||
|
//s1 --State
|
||||||
|
//s1 --State
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
//SECTION - Stateless functions
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Action Callbacks
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//SECTION - Build Setup
|
||||||
|
//s1 -Values
|
||||||
|
_config = widget.sheetConfiguration ?? const AstromicSheetConfiguration();
|
||||||
|
_style = widget.sheetStyle ?? const AstromicSheetStyle();
|
||||||
|
//s1 -Values
|
||||||
|
|
||||||
|
//s1 -Widgets
|
||||||
|
List<Widget> flexChildren = <Widget>[
|
||||||
|
widget.headSection != null
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: _style.headSectionPadding,
|
||||||
|
child: widget.headSection,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Container(
|
||||||
|
color: _style.contentSectionColor,
|
||||||
|
padding: _style.contentSectionPadding,
|
||||||
|
child: widget.contentSection != null ? widget.contentSection! : Container(),
|
||||||
|
),
|
||||||
|
widget.footerSection != null
|
||||||
|
? Container(
|
||||||
|
color: _style.footerSectionColor,
|
||||||
|
padding: _style.footerSectionPadding,
|
||||||
|
child: widget.footerSection,
|
||||||
|
)
|
||||||
|
: Container()
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Widget> formChildren = <Widget>[
|
||||||
|
widget.headSectionFormBuilder != null
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: _style.headSectionPadding,
|
||||||
|
child: widget.headSectionFormBuilder!(_formController),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Container(
|
||||||
|
color: _style.contentSectionColor,
|
||||||
|
padding: _style.contentSectionPadding,
|
||||||
|
child: widget.contentSectionFormBuilder != null ? widget.contentSectionFormBuilder!(_formController) : Container(),
|
||||||
|
),
|
||||||
|
widget.footerSectionFormBuilder != null
|
||||||
|
? Container(
|
||||||
|
color: _style.footerSectionColor,
|
||||||
|
padding: _style.footerSectionPadding,
|
||||||
|
child: widget.footerSectionFormBuilder != null ? widget.footerSectionFormBuilder!(_formController) : Container(),
|
||||||
|
)
|
||||||
|
: Container()
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Widget> scrollerChildren = <Widget>[
|
||||||
|
widget.headSectionScrollerBuilder != null
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _style.headSectionColor,
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: _style.headSectionPadding,
|
||||||
|
width: double.infinity,
|
||||||
|
child: widget.headSectionScrollerBuilder!(widget.scrollController!),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: _style.contentSectionPadding,
|
||||||
|
width: double.infinity,
|
||||||
|
color: _style.contentSectionColor,
|
||||||
|
child: widget.contentSectionScrollBuilder != null ? widget.contentSectionScrollBuilder!(widget.scrollController!, widget.scrollPhysics!) : Container(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
widget.footerSectionScrollerBuilder != null
|
||||||
|
? Container(
|
||||||
|
color: _style.footerSectionColor,
|
||||||
|
padding: _style.footerSectionPadding,
|
||||||
|
child: widget.footerSectionScrollerBuilder!(widget.scrollController!),
|
||||||
|
)
|
||||||
|
: Container()
|
||||||
|
];
|
||||||
|
//s1 -Widgets
|
||||||
|
//!SECTION
|
||||||
|
|
||||||
|
//SECTION - Build Return
|
||||||
|
return PopScope(
|
||||||
|
canPop: _config.dismissOnBack || _config.enableOutsideInteraction || _config.enableSheetInteraction,
|
||||||
|
child: Container(
|
||||||
|
child:
|
||||||
|
//FLEX
|
||||||
|
widget.sheetType == SheetType.flex
|
||||||
|
? Container(
|
||||||
|
padding: EdgeInsets.only(bottom: _config.respectKeyboardInset ? MediaQuery.of(context).viewInsets.bottom : 0),
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _style.headSectionColor,
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: flexChildren,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
:
|
||||||
|
//FORM
|
||||||
|
widget.sheetType == SheetType.form
|
||||||
|
? Container(
|
||||||
|
padding: EdgeInsets.only(bottom: _config.respectKeyboardInset ? MediaQuery.of(context).viewInsets.bottom : 0),
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _style.headSectionColor,
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Form(
|
||||||
|
key: _formController.key,
|
||||||
|
child: Column(
|
||||||
|
children: formChildren,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
:
|
||||||
|
//Scroller
|
||||||
|
widget.sheetType == SheetType.scroller
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(_style.radius),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: scrollerChildren,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
//!SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
//SECTION - Disposable variables
|
||||||
|
_formController.dispose();
|
||||||
|
//!SECTION
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
63
lib/src/sheet/src/widgets/basic.sheet.dart
Normal file
63
lib/src/sheet/src/widgets/basic.sheet.dart
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
import '../../../../dependencies/vsync-provider/vsync_provider.dart';
|
||||||
|
//s3 Models
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class BasicSheet {
|
||||||
|
//
|
||||||
|
static Future<T?> show<T extends Object?>({
|
||||||
|
required BuildContext context,
|
||||||
|
required VsyncProviderState vsyncState,
|
||||||
|
required Widget child,
|
||||||
|
//
|
||||||
|
bool useRootNavigator = false,
|
||||||
|
bool enableSheetInteraction = true,
|
||||||
|
bool enableOutsideInteraction = true,
|
||||||
|
//
|
||||||
|
Color barrierColor = const Color(0xb3212121),
|
||||||
|
double radius = 24,
|
||||||
|
//
|
||||||
|
Duration forwardAnimationDuration = const Duration(milliseconds: 250),
|
||||||
|
Duration reverseAnimationDuration = const Duration(milliseconds: 250),
|
||||||
|
Curve? animationCurve,
|
||||||
|
//
|
||||||
|
}) async {
|
||||||
|
AnimationController controller = AnimationController(
|
||||||
|
vsync: vsyncState,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Animation duration for displaying the BottomSheet
|
||||||
|
controller.duration = forwardAnimationDuration;
|
||||||
|
|
||||||
|
// Animation duration for retracting the BottomSheet
|
||||||
|
controller.reverseDuration = reverseAnimationDuration;
|
||||||
|
|
||||||
|
// Set animation curve duration for the BottomSheet
|
||||||
|
controller.drive(CurveTween(curve: animationCurve ?? Curves.easeOut));
|
||||||
|
|
||||||
|
return await showModalBottomSheet<T?>(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
barrierColor: barrierColor,
|
||||||
|
//
|
||||||
|
enableDrag: enableSheetInteraction,
|
||||||
|
isDismissible: enableOutsideInteraction,
|
||||||
|
useRootNavigator: useRootNavigator,
|
||||||
|
//
|
||||||
|
isScrollControlled: true,
|
||||||
|
transitionAnimationController: controller,
|
||||||
|
//
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext c) {
|
||||||
|
return Wrap(children: <Widget>[ClipRRect(borderRadius: BorderRadius.vertical(top: Radius.circular(radius)), child: child)]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
286
lib/src/sheet/src/widgets/scroller.sheet.dart
Normal file
286
lib/src/sheet/src/widgets/scroller.sheet.dart
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
//s1 Imports
|
||||||
|
//s2 Core Package Imports
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
//s2 1st-party Package Imports
|
||||||
|
//s2 3rd-party Package Imports
|
||||||
|
//s2 Dependancies Imports
|
||||||
|
//s3 Routes
|
||||||
|
//s3 Services
|
||||||
|
import '../../../../dependencies/vsync-provider/vsync_provider.dart';
|
||||||
|
//s3 Models
|
||||||
|
//s1 Exports
|
||||||
|
|
||||||
|
typedef StopperBuilder = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
ScrollController controller,
|
||||||
|
ScrollPhysics physics,
|
||||||
|
int stop,
|
||||||
|
);
|
||||||
|
|
||||||
|
class ScrollerSheet<T extends Object?> extends StatefulWidget {
|
||||||
|
//
|
||||||
|
final List<double>? stops;
|
||||||
|
final StopperBuilder? builder;
|
||||||
|
final int initialStop;
|
||||||
|
final double dragThreshold;
|
||||||
|
final Color? barrierColor;
|
||||||
|
final double? topInset;
|
||||||
|
//
|
||||||
|
final ScrollPhysics? physics;
|
||||||
|
final bool? enableSheetInteraction;
|
||||||
|
//
|
||||||
|
ScrollerSheet({
|
||||||
|
super.key,
|
||||||
|
required this.builder,
|
||||||
|
required this.stops,
|
||||||
|
this.initialStop = 0,
|
||||||
|
this.barrierColor = Colors.black38,
|
||||||
|
this.dragThreshold = 25,
|
||||||
|
this.topInset,
|
||||||
|
this.physics,
|
||||||
|
this.enableSheetInteraction = true,
|
||||||
|
//
|
||||||
|
}) : assert(initialStop < stops!.length);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ScrollerSheetState<T> createState() => ScrollerSheetState<T>();
|
||||||
|
|
||||||
|
static Future<T?> show<T extends Object?>({
|
||||||
|
Key? key,
|
||||||
|
required BuildContext context,
|
||||||
|
required VsyncProviderState vsyncState,
|
||||||
|
required StopperBuilder? builder,
|
||||||
|
//
|
||||||
|
bool useRootNavigator = true,
|
||||||
|
bool enableSheetInteraction = true,
|
||||||
|
bool enableOutsideInteraction = true,
|
||||||
|
bool respectKeyboardInset = true,
|
||||||
|
ScrollPhysics? physics = const BouncingScrollPhysics(),
|
||||||
|
//
|
||||||
|
required List<double>? stops,
|
||||||
|
int initialStop = 0,
|
||||||
|
double dragThreshold = 25,
|
||||||
|
//
|
||||||
|
Duration? forwardAnimationDuration = const Duration(milliseconds: 250),
|
||||||
|
Duration? reverseAnimationDuration = const Duration(milliseconds: 250),
|
||||||
|
Curve? animationCurve,
|
||||||
|
//
|
||||||
|
Color barrierColor = const Color(0xb3212121),
|
||||||
|
double? topInset,
|
||||||
|
}) async {
|
||||||
|
Future<T?>? ret;
|
||||||
|
AnimationController controller = AnimationController(
|
||||||
|
vsync: vsyncState,
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.duration = forwardAnimationDuration;
|
||||||
|
controller.reverseDuration = reverseAnimationDuration;
|
||||||
|
controller.drive(CurveTween(curve: animationCurve ?? Curves.easeOut));
|
||||||
|
|
||||||
|
ret = showModalBottomSheet<T?>(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
barrierColor: barrierColor,
|
||||||
|
//
|
||||||
|
enableDrag: enableSheetInteraction,
|
||||||
|
isDismissible: enableOutsideInteraction,
|
||||||
|
//
|
||||||
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: useRootNavigator,
|
||||||
|
transitionAnimationController: controller,
|
||||||
|
//
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: respectKeyboardInset ? MediaQuery.of(context).viewInsets.bottom : 0,
|
||||||
|
),
|
||||||
|
child: ScrollerSheet<T>(
|
||||||
|
key: key,
|
||||||
|
builder: builder,
|
||||||
|
stops: stops,
|
||||||
|
initialStop: initialStop,
|
||||||
|
dragThreshold: dragThreshold,
|
||||||
|
physics: physics,
|
||||||
|
topInset: topInset,
|
||||||
|
enableSheetInteraction: enableSheetInteraction,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScrollerSheetState<T> extends State<ScrollerSheet<T>> with SingleTickerProviderStateMixin {
|
||||||
|
List<double>? _stops;
|
||||||
|
int? _currentStop;
|
||||||
|
int? _targetStop;
|
||||||
|
bool _dragging = false;
|
||||||
|
bool _closing = false;
|
||||||
|
double? _dragOffset;
|
||||||
|
double? _closingHeight;
|
||||||
|
ScrollController? _scrollController;
|
||||||
|
ScrollPhysics? _scrollPhysics;
|
||||||
|
Animation<double>? _animation;
|
||||||
|
AnimationController? _animationController;
|
||||||
|
Tween<double>? _tween;
|
||||||
|
|
||||||
|
ScrollPhysics _getScrollPhysicsForStop(dynamic s) {
|
||||||
|
if (s == _stops!.length - 1) {
|
||||||
|
return widget.physics ?? const BouncingScrollPhysics();
|
||||||
|
} else {
|
||||||
|
return const NeverScrollableScrollPhysics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_stops = widget.stops;
|
||||||
|
_currentStop = widget.initialStop;
|
||||||
|
_targetStop = _currentStop;
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
_scrollPhysics = _getScrollPhysicsForStop(_currentStop);
|
||||||
|
_animationController = AnimationController(vsync: this, duration: const Duration(seconds: 2));
|
||||||
|
final CurvedAnimation curveAnimation = CurvedAnimation(parent: _animationController!, curve: Curves.linear);
|
||||||
|
_tween = Tween<double>(begin: _stops?[_currentStop!], end: _stops?[_targetStop!]);
|
||||||
|
_animation = _tween?.animate(curveAnimation);
|
||||||
|
_scrollController?.addListener(() {
|
||||||
|
if (_scrollController!.offset < -widget.dragThreshold) {
|
||||||
|
if (_currentStop != _targetStop || _dragging) return;
|
||||||
|
if (_currentStop! > 0) {
|
||||||
|
double h0 = height;
|
||||||
|
_targetStop = _currentStop! - 1;
|
||||||
|
_animate(h0, _stops![_targetStop!]);
|
||||||
|
} else if (!_closing) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(ScrollerSheet<T> oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
_stops = widget.stops;
|
||||||
|
_currentStop = min(_currentStop!, _stops!.length - 1);
|
||||||
|
_targetStop = min(_currentStop!, _stops!.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_animationController!.dispose();
|
||||||
|
_scrollController!.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
get stop => _currentStop;
|
||||||
|
|
||||||
|
set stop(dynamic nextStop) {
|
||||||
|
_targetStop = max(0, min(_stops!.length - 1, nextStop));
|
||||||
|
_animate(height, nextStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get canClose {
|
||||||
|
return widget.enableSheetInteraction!;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close({T? result}) {
|
||||||
|
if (!_closing && canClose) {
|
||||||
|
_closingHeight = height;
|
||||||
|
_animationController!.stop();
|
||||||
|
_dragging = false;
|
||||||
|
_closing = true;
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _animate(double from, double to) {
|
||||||
|
_tween!.begin = from;
|
||||||
|
_tween!.end = to;
|
||||||
|
_animationController!.value = 0;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||||
|
if (_scrollController != null && _scrollController!.hasClients) {
|
||||||
|
if (_scrollController!.offset < 0) {
|
||||||
|
_scrollController!.animateTo(0, duration: const Duration(seconds: 200), curve: Curves.linear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_animationController!.fling().then((_) {
|
||||||
|
_currentStop = _targetStop;
|
||||||
|
setState(() {
|
||||||
|
_scrollPhysics = _getScrollPhysicsForStop(_currentStop);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get height {
|
||||||
|
if (_closing) {
|
||||||
|
return _closingHeight;
|
||||||
|
} else if (_dragging) {
|
||||||
|
return _stops![_currentStop!] + _dragOffset!;
|
||||||
|
} else if (_animationController!.isAnimating) {
|
||||||
|
return _animation!.value;
|
||||||
|
} else {
|
||||||
|
return _stops![_currentStop!];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: widget.topInset ?? 0,
|
||||||
|
),
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _animation!,
|
||||||
|
child: GestureDetector(
|
||||||
|
onVerticalDragStart: (DragStartDetails details) {
|
||||||
|
if (_currentStop != _targetStop) return;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||||
|
if (_scrollController != null && _scrollController!.hasClients) {
|
||||||
|
_scrollController!.jumpTo(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_dragging = true;
|
||||||
|
_dragOffset = 0;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
onVerticalDragUpdate: (DragUpdateDetails details) {
|
||||||
|
if (_dragging) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||||
|
if (_scrollController != null && _scrollController!.hasClients) {
|
||||||
|
_scrollController!.jumpTo(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_dragOffset = _dragOffset! - details.delta.dy;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onVerticalDragEnd: (DragEndDetails details) {
|
||||||
|
if (!_dragging || _closing) return;
|
||||||
|
if (_dragOffset! > widget.dragThreshold) {
|
||||||
|
_targetStop = min(_currentStop! + 1, _stops!.length - 1);
|
||||||
|
} else if (_dragOffset! < -widget.dragThreshold) {
|
||||||
|
_targetStop = max(canClose ? -1 : 0, _currentStop! - 1);
|
||||||
|
}
|
||||||
|
if (_targetStop! < 0) {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
_dragging = false;
|
||||||
|
_animate(_stops![_currentStop!] + _dragOffset!, _stops![_targetStop!]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: widget.builder!(context, _scrollController!, _scrollPhysics!, _currentStop!),
|
||||||
|
),
|
||||||
|
builder: (BuildContext context, Widget? child) {
|
||||||
|
return SizedBox(
|
||||||
|
height: min(_stops![_stops!.length - 1], max(0, height)),
|
||||||
|
child: ClipRRect(child: Container(color: Colors.transparent, child: child)),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
lib/src/sheet/src/widgets/widgets.exports.dart
Normal file
3
lib/src/sheet/src/widgets/widgets.exports.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export 'base.dart';
|
||||||
|
export 'basic.sheet.dart';
|
||||||
|
export 'scroller.sheet.dart';
|
||||||
490
pubspec.lock
Normal file
490
pubspec.lock
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.0"
|
||||||
|
astromic_elements:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: master
|
||||||
|
resolved-ref: "2f77d5d81328298952d3f07092d2aea2e6c11a44"
|
||||||
|
url: "https://git.micazi.dev/micazi/astromic_elements.git"
|
||||||
|
source: git
|
||||||
|
version: "0.1.0"
|
||||||
|
astromic_extensions:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: HEAD
|
||||||
|
resolved-ref: bccde3fe9f3d47b29e08ac6d287c2bc06f6d855c
|
||||||
|
url: "https://stag-git.micazi.dev/micazi/astromic_extensions.git"
|
||||||
|
source: git
|
||||||
|
version: "0.1.0"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.13.0"
|
||||||
|
back_button_interceptor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: back_button_interceptor
|
||||||
|
sha256: b85977faabf4aeb95164b3b8bf81784bed4c54ea1aef90a036ab6927ecf80c5a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.4"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.1"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.6"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.17"
|
||||||
|
flutter_switch:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_switch
|
||||||
|
sha256: b91477f926bba135d2d203d7b24367492662d8d9c3aa6adb960b14c1087d3c41
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.2"
|
||||||
|
form_controller:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: form_controller
|
||||||
|
sha256: "5e0f905cc8802ac787bd295900a122fa2a0b1a26070477666de0b9c3b4263e0c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.8+2"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.19.0"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
loader_overlay:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: loader_overlay
|
||||||
|
sha256: "285c9ccab9a42a392ba948bd0b14376fd0ee9ddd7b63e3018bcd54460fd3e021"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.1"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.16.0"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.15"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.2"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.6"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.28.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.1"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
sqflite_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_android
|
||||||
|
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4+6"
|
||||||
|
sqflite_darwin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_darwin
|
||||||
|
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1+1"
|
||||||
|
sqflite_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_platform_interface
|
||||||
|
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0+3"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.1"
|
||||||
|
vector_graphics:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics
|
||||||
|
sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.18"
|
||||||
|
vector_graphics_codec:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_codec
|
||||||
|
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.13"
|
||||||
|
vector_graphics_compiler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_compiler
|
||||||
|
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.16"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.5.0"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.7.0-0 <4.0.0"
|
||||||
|
flutter: ">3.27.0"
|
||||||
25
pubspec.yaml
Normal file
25
pubspec.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: astromic_helpers
|
||||||
|
description: The helpers module of the Astromic Presentation System
|
||||||
|
publish_to: "none"
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.6.0"
|
||||||
|
flutter: ">3.27.0"
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_lints: ^4.0.0
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
##S1 ~ Astromic
|
||||||
|
astromic_elements:
|
||||||
|
git:
|
||||||
|
url: https://git.micazi.dev/micazi/astromic_elements.git
|
||||||
|
ref: master
|
||||||
|
##S1 ~ 3rd-party
|
||||||
|
## Needed for the form helper.
|
||||||
|
form_controller: ^0.8.8+2
|
||||||
|
## Needed for the loading helper.
|
||||||
|
loader_overlay: ^5.0.0
|
||||||
Reference in New Issue
Block a user