mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-08-15 01:15:42 -04:00
break everything
This commit is contained in:
parent
e898074387
commit
29210c89d2
121 changed files with 2892 additions and 2608 deletions
51
lib/tools/async_table_db_backed_cubit.dart
Normal file
51
lib/tools/async_table_db_backed_cubit.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
import '../../tools/tools.dart';
|
||||
import '../../veilid_init.dart';
|
||||
import '../../veilid_support/veilid_support.dart';
|
||||
|
||||
abstract class AsyncTableDBBackedCubit<State> extends Cubit<AsyncValue<State>>
|
||||
with TableDBBacked<State> {
|
||||
AsyncTableDBBackedCubit() : super(const AsyncValue.loading()) {
|
||||
unawaited(Future.delayed(Duration.zero, _build));
|
||||
}
|
||||
|
||||
Future<void> _build() async {
|
||||
try {
|
||||
await eventualVeilid.future;
|
||||
emit(AsyncValue.data(await load()));
|
||||
} on Exception catch (e, stackTrace) {
|
||||
emit(AsyncValue.error(e, stackTrace));
|
||||
}
|
||||
}
|
||||
|
||||
Future<State> readyData() async {
|
||||
final stateStream = stream.distinct();
|
||||
await for (final AsyncValue<State> av in stateStream) {
|
||||
final d = av.when(
|
||||
data: (value) => value, loading: () => null, error: (e, s) => null);
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
final ef = av.when(
|
||||
data: (value) => null,
|
||||
loading: () => null,
|
||||
error: Future<State>.error);
|
||||
if (ef != null) {
|
||||
return ef;
|
||||
}
|
||||
}
|
||||
return Future<State>.error(
|
||||
StateError("data never became ready in cubit '$runtimeType'"));
|
||||
}
|
||||
|
||||
Future<void> setState(State newState) async {
|
||||
try {
|
||||
emit(AsyncValue.data(await store(newState)));
|
||||
} on Exception catch (e, stackTrace) {
|
||||
emit(AsyncValue.error(e, stackTrace));
|
||||
}
|
||||
}
|
||||
}
|
172
lib/tools/async_value.dart
Normal file
172
lib/tools/async_value.dart
Normal file
|
@ -0,0 +1,172 @@
|
|||
// ignore_for_file: avoid_catches_without_on_clauses
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'async_value.freezed.dart';
|
||||
|
||||
/// An utility for safely manipulating asynchronous data.
|
||||
///
|
||||
/// By using [AsyncValue], you are guaranteed that you cannot forget to
|
||||
/// handle the loading/error state of an asynchronous operation.
|
||||
///
|
||||
/// It also expose some utilities to nicely convert an [AsyncValue] to
|
||||
/// a different object.
|
||||
/// For example, a Flutter Widget may use [when] to convert an [AsyncValue]
|
||||
/// into either a progress indicator, an error screen, or to show the data:
|
||||
///
|
||||
/// ```dart
|
||||
/// /// A provider that asynchronously expose the current user
|
||||
/// final userProvider = StreamProvider<User>((_) async* {
|
||||
/// // fetch the user
|
||||
/// });
|
||||
///
|
||||
/// class Example extends ConsumerWidget {
|
||||
/// @override
|
||||
/// Widget build(BuildContext context, ScopedReader watch) {
|
||||
/// final AsyncValue<User> user = watch(userProvider);
|
||||
///
|
||||
/// return user.when(
|
||||
/// loading: () => CircularProgressIndicator(),
|
||||
/// error: (error, stack) => Text('Oops, something unexpected happened'),
|
||||
/// data: (value) => Text('Hello ${user.name}'),
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If a consumer of an [AsyncValue] does not care about the loading/error
|
||||
/// state, consider using [data] to read the state:
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context, ScopedReader watch) {
|
||||
/// // reads the data state directly – will be null during loading/error states
|
||||
/// final User user = watch(userProvider).data?.value;
|
||||
///
|
||||
/// return Text('Hello ${user?.name}');
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [AsyncValue.guard], to simplify transforming a [Future] into an
|
||||
/// [AsyncValue].
|
||||
/// - The package Freezed (https://github.com/rrousselgit/freezed), which have
|
||||
/// generated this [AsyncValue] class and explains how [map]/[when] works.
|
||||
@freezed
|
||||
@sealed
|
||||
abstract class AsyncValue<T> with _$AsyncValue<T> {
|
||||
const AsyncValue._();
|
||||
|
||||
/// Creates an [AsyncValue] with a data.
|
||||
///
|
||||
/// The data can be `null`.
|
||||
const factory AsyncValue.data(T value) = AsyncData<T>;
|
||||
|
||||
/// Creates an [AsyncValue] in loading state.
|
||||
///
|
||||
/// Prefer always using this constructor with the `const` keyword.
|
||||
const factory AsyncValue.loading() = AsyncLoading<T>;
|
||||
|
||||
/// Creates an [AsyncValue] in error state.
|
||||
///
|
||||
/// The parameter [error] cannot be `null`.
|
||||
factory AsyncValue.error(Object error, [StackTrace? stackTrace]) =
|
||||
AsyncError<T>;
|
||||
|
||||
/// Transforms a [Future] that may fail into something that is safe to read.
|
||||
///
|
||||
/// This is useful to avoid having to do a tedious `try/catch`. Instead of:
|
||||
///
|
||||
/// ```dart
|
||||
/// class MyNotifier extends StateNotifier<AsyncValue<MyData> {
|
||||
/// MyNotifier(): super(const AsyncValue.loading()) {
|
||||
/// _fetchData();
|
||||
/// }
|
||||
///
|
||||
/// Future<void> _fetchData() async {
|
||||
/// state = const AsyncValue.loading();
|
||||
/// try {
|
||||
/// final response = await dio.get('my_api/data');
|
||||
/// final data = MyData.fromJson(response);
|
||||
/// state = AsyncValue.data(data);
|
||||
/// } catch (err, stack) {
|
||||
/// state = AsyncValue.error(err, stack);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// which is redundant as the application grows and we need more and more of
|
||||
/// this pattern – we can use [guard] to simplify it:
|
||||
///
|
||||
///
|
||||
/// ```dart
|
||||
/// class MyNotifier extends StateNotifier<AsyncValue<MyData>> {
|
||||
/// MyNotifier(): super(const AsyncValue.loading()) {
|
||||
/// _fetchData();
|
||||
/// }
|
||||
///
|
||||
/// Future<void> _fetchData() async {
|
||||
/// state = const AsyncValue.loading();
|
||||
/// // does the try/catch for us like previously
|
||||
/// state = await AsyncValue.guard(() async {
|
||||
/// final response = await dio.get('my_api/data');
|
||||
/// return Data.fromJson(response);
|
||||
/// });
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
static Future<AsyncValue<T>> guard<T>(Future<T> Function() future) async {
|
||||
try {
|
||||
return AsyncValue.data(await future());
|
||||
} catch (err, stack) {
|
||||
return AsyncValue.error(err, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// The current data, or null if in loading/error.
|
||||
///
|
||||
/// This is safe to use, as Dart (will) have non-nullable types.
|
||||
/// As such reading [data] still forces to handle the loading/error cases
|
||||
/// by having to check `data != null`.
|
||||
///
|
||||
/// ## Why does [AsyncValue<T>.data] return [AsyncData<T>] instead of [T]?
|
||||
///
|
||||
/// The motivation behind this decision is to allow differentiating between:
|
||||
///
|
||||
/// - There is a data, and it is `null`.
|
||||
/// ```dart
|
||||
/// // There is a data, and it is "null"
|
||||
/// AsyncValue<Configuration> configs = AsyncValue.data(null);
|
||||
///
|
||||
/// print(configs.data); // AsyncValue(value: null)
|
||||
/// print(configs.data.value); // null
|
||||
/// ```
|
||||
///
|
||||
/// - There is no data. [AsyncValue] is currently in loading/error state.
|
||||
/// ```dart
|
||||
/// // No data, currently loading
|
||||
/// AsyncValue<Configuration> configs = AsyncValue.loading();
|
||||
///
|
||||
/// print(configs.data); // null, currently loading
|
||||
/// print(configs.data.value); // throws null exception
|
||||
/// ```
|
||||
AsyncData<T>? get data => map(
|
||||
data: (data) => data,
|
||||
loading: (_) => null,
|
||||
error: (_) => null,
|
||||
);
|
||||
|
||||
/// Shorthand for [when] to handle only the `data` case.
|
||||
AsyncValue<R> whenData<R>(R Function(T value) cb) => when(
|
||||
data: (value) {
|
||||
try {
|
||||
return AsyncValue.data(cb(value));
|
||||
} catch (err, stack) {
|
||||
return AsyncValue.error(err, stack);
|
||||
}
|
||||
},
|
||||
loading: () => const AsyncValue.loading(),
|
||||
error: AsyncValue.error,
|
||||
);
|
||||
}
|
480
lib/tools/async_value.freezed.dart
Normal file
480
lib/tools/async_value.freezed.dart
Normal file
|
@ -0,0 +1,480 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'async_value.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AsyncValue<T> {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(T value) data,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(Object error, StackTrace? stackTrace) error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(T value)? data,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(Object error, StackTrace? stackTrace)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(T value)? data,
|
||||
TResult Function()? loading,
|
||||
TResult Function(Object error, StackTrace? stackTrace)? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(AsyncData<T> value) data,
|
||||
required TResult Function(AsyncLoading<T> value) loading,
|
||||
required TResult Function(AsyncError<T> value) error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(AsyncData<T> value)? data,
|
||||
TResult? Function(AsyncLoading<T> value)? loading,
|
||||
TResult? Function(AsyncError<T> value)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(AsyncData<T> value)? data,
|
||||
TResult Function(AsyncLoading<T> value)? loading,
|
||||
TResult Function(AsyncError<T> value)? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AsyncValueCopyWith<T, $Res> {
|
||||
factory $AsyncValueCopyWith(
|
||||
AsyncValue<T> value, $Res Function(AsyncValue<T>) then) =
|
||||
_$AsyncValueCopyWithImpl<T, $Res, AsyncValue<T>>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$AsyncValueCopyWithImpl<T, $Res, $Val extends AsyncValue<T>>
|
||||
implements $AsyncValueCopyWith<T, $Res> {
|
||||
_$AsyncValueCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AsyncDataImplCopyWith<T, $Res> {
|
||||
factory _$$AsyncDataImplCopyWith(
|
||||
_$AsyncDataImpl<T> value, $Res Function(_$AsyncDataImpl<T>) then) =
|
||||
__$$AsyncDataImplCopyWithImpl<T, $Res>;
|
||||
@useResult
|
||||
$Res call({T value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AsyncDataImplCopyWithImpl<T, $Res>
|
||||
extends _$AsyncValueCopyWithImpl<T, $Res, _$AsyncDataImpl<T>>
|
||||
implements _$$AsyncDataImplCopyWith<T, $Res> {
|
||||
__$$AsyncDataImplCopyWithImpl(
|
||||
_$AsyncDataImpl<T> _value, $Res Function(_$AsyncDataImpl<T>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_$AsyncDataImpl<T>(
|
||||
freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as T,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$AsyncDataImpl<T> extends AsyncData<T> {
|
||||
const _$AsyncDataImpl(this.value) : super._();
|
||||
|
||||
@override
|
||||
final T value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AsyncValue<$T>.data(value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AsyncDataImpl<T> &&
|
||||
const DeepCollectionEquality().equals(other.value, value));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(value));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AsyncDataImplCopyWith<T, _$AsyncDataImpl<T>> get copyWith =>
|
||||
__$$AsyncDataImplCopyWithImpl<T, _$AsyncDataImpl<T>>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(T value) data,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(Object error, StackTrace? stackTrace) error,
|
||||
}) {
|
||||
return data(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(T value)? data,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(Object error, StackTrace? stackTrace)? error,
|
||||
}) {
|
||||
return data?.call(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(T value)? data,
|
||||
TResult Function()? loading,
|
||||
TResult Function(Object error, StackTrace? stackTrace)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (data != null) {
|
||||
return data(value);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(AsyncData<T> value) data,
|
||||
required TResult Function(AsyncLoading<T> value) loading,
|
||||
required TResult Function(AsyncError<T> value) error,
|
||||
}) {
|
||||
return data(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(AsyncData<T> value)? data,
|
||||
TResult? Function(AsyncLoading<T> value)? loading,
|
||||
TResult? Function(AsyncError<T> value)? error,
|
||||
}) {
|
||||
return data?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(AsyncData<T> value)? data,
|
||||
TResult Function(AsyncLoading<T> value)? loading,
|
||||
TResult Function(AsyncError<T> value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (data != null) {
|
||||
return data(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsyncData<T> extends AsyncValue<T> {
|
||||
const factory AsyncData(final T value) = _$AsyncDataImpl<T>;
|
||||
const AsyncData._() : super._();
|
||||
|
||||
T get value;
|
||||
@JsonKey(ignore: true)
|
||||
_$$AsyncDataImplCopyWith<T, _$AsyncDataImpl<T>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AsyncLoadingImplCopyWith<T, $Res> {
|
||||
factory _$$AsyncLoadingImplCopyWith(_$AsyncLoadingImpl<T> value,
|
||||
$Res Function(_$AsyncLoadingImpl<T>) then) =
|
||||
__$$AsyncLoadingImplCopyWithImpl<T, $Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AsyncLoadingImplCopyWithImpl<T, $Res>
|
||||
extends _$AsyncValueCopyWithImpl<T, $Res, _$AsyncLoadingImpl<T>>
|
||||
implements _$$AsyncLoadingImplCopyWith<T, $Res> {
|
||||
__$$AsyncLoadingImplCopyWithImpl(
|
||||
_$AsyncLoadingImpl<T> _value, $Res Function(_$AsyncLoadingImpl<T>) _then)
|
||||
: super(_value, _then);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$AsyncLoadingImpl<T> extends AsyncLoading<T> {
|
||||
const _$AsyncLoadingImpl() : super._();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AsyncValue<$T>.loading()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$AsyncLoadingImpl<T>);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(T value) data,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(Object error, StackTrace? stackTrace) error,
|
||||
}) {
|
||||
return loading();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(T value)? data,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(Object error, StackTrace? stackTrace)? error,
|
||||
}) {
|
||||
return loading?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(T value)? data,
|
||||
TResult Function()? loading,
|
||||
TResult Function(Object error, StackTrace? stackTrace)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loading != null) {
|
||||
return loading();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(AsyncData<T> value) data,
|
||||
required TResult Function(AsyncLoading<T> value) loading,
|
||||
required TResult Function(AsyncError<T> value) error,
|
||||
}) {
|
||||
return loading(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(AsyncData<T> value)? data,
|
||||
TResult? Function(AsyncLoading<T> value)? loading,
|
||||
TResult? Function(AsyncError<T> value)? error,
|
||||
}) {
|
||||
return loading?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(AsyncData<T> value)? data,
|
||||
TResult Function(AsyncLoading<T> value)? loading,
|
||||
TResult Function(AsyncError<T> value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loading != null) {
|
||||
return loading(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsyncLoading<T> extends AsyncValue<T> {
|
||||
const factory AsyncLoading() = _$AsyncLoadingImpl<T>;
|
||||
const AsyncLoading._() : super._();
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AsyncErrorImplCopyWith<T, $Res> {
|
||||
factory _$$AsyncErrorImplCopyWith(
|
||||
_$AsyncErrorImpl<T> value, $Res Function(_$AsyncErrorImpl<T>) then) =
|
||||
__$$AsyncErrorImplCopyWithImpl<T, $Res>;
|
||||
@useResult
|
||||
$Res call({Object error, StackTrace? stackTrace});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AsyncErrorImplCopyWithImpl<T, $Res>
|
||||
extends _$AsyncValueCopyWithImpl<T, $Res, _$AsyncErrorImpl<T>>
|
||||
implements _$$AsyncErrorImplCopyWith<T, $Res> {
|
||||
__$$AsyncErrorImplCopyWithImpl(
|
||||
_$AsyncErrorImpl<T> _value, $Res Function(_$AsyncErrorImpl<T>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? error = null,
|
||||
Object? stackTrace = freezed,
|
||||
}) {
|
||||
return _then(_$AsyncErrorImpl<T>(
|
||||
null == error ? _value.error : error,
|
||||
freezed == stackTrace
|
||||
? _value.stackTrace
|
||||
: stackTrace // ignore: cast_nullable_to_non_nullable
|
||||
as StackTrace?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$AsyncErrorImpl<T> extends AsyncError<T> {
|
||||
_$AsyncErrorImpl(this.error, [this.stackTrace]) : super._();
|
||||
|
||||
@override
|
||||
final Object error;
|
||||
@override
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AsyncValue<$T>.error(error: $error, stackTrace: $stackTrace)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AsyncErrorImpl<T> &&
|
||||
const DeepCollectionEquality().equals(other.error, error) &&
|
||||
(identical(other.stackTrace, stackTrace) ||
|
||||
other.stackTrace == stackTrace));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, const DeepCollectionEquality().hash(error), stackTrace);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AsyncErrorImplCopyWith<T, _$AsyncErrorImpl<T>> get copyWith =>
|
||||
__$$AsyncErrorImplCopyWithImpl<T, _$AsyncErrorImpl<T>>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(T value) data,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(Object error, StackTrace? stackTrace) error,
|
||||
}) {
|
||||
return error(this.error, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(T value)? data,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(Object error, StackTrace? stackTrace)? error,
|
||||
}) {
|
||||
return error?.call(this.error, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(T value)? data,
|
||||
TResult Function()? loading,
|
||||
TResult Function(Object error, StackTrace? stackTrace)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (error != null) {
|
||||
return error(this.error, stackTrace);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(AsyncData<T> value) data,
|
||||
required TResult Function(AsyncLoading<T> value) loading,
|
||||
required TResult Function(AsyncError<T> value) error,
|
||||
}) {
|
||||
return error(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(AsyncData<T> value)? data,
|
||||
TResult? Function(AsyncLoading<T> value)? loading,
|
||||
TResult? Function(AsyncError<T> value)? error,
|
||||
}) {
|
||||
return error?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(AsyncData<T> value)? data,
|
||||
TResult Function(AsyncLoading<T> value)? loading,
|
||||
TResult Function(AsyncError<T> value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (error != null) {
|
||||
return error(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsyncError<T> extends AsyncValue<T> {
|
||||
factory AsyncError(final Object error, [final StackTrace? stackTrace]) =
|
||||
_$AsyncErrorImpl<T>;
|
||||
AsyncError._() : super._();
|
||||
|
||||
Object get error;
|
||||
StackTrace? get stackTrace;
|
||||
@JsonKey(ignore: true)
|
||||
_$$AsyncErrorImplCopyWith<T, _$AsyncErrorImpl<T>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:ansicolor/ansicolor.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:loggy/loggy.dart';
|
||||
|
||||
import '../pages/developer.dart';
|
||||
import '../old_to_refactor/pages/developer.dart';
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
import 'state_logger.dart';
|
||||
|
||||
String wrapWithLogColor(LogLevel? level, String text) {
|
||||
// XXX: https://github.com/flutter/flutter/issues/64491
|
||||
|
@ -149,4 +151,7 @@ void initLoggy() {
|
|||
}
|
||||
|
||||
Loggy('').level = getLogOptions(logLevel);
|
||||
|
||||
// Create state logger
|
||||
Bloc.observer = const StateLogger();
|
||||
}
|
||||
|
|
|
@ -1,551 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
import 'package:radix_colors/radix_colors.dart';
|
||||
|
||||
import 'theme_service.dart';
|
||||
|
||||
enum RadixThemeColor {
|
||||
scarlet, // tomato + red + violet
|
||||
babydoll, // crimson + purple + pink
|
||||
vapor, // pink + cyan + plum
|
||||
gold, // yellow + amber + orange
|
||||
garden, // grass + orange + brown
|
||||
forest, // green + brown + amber
|
||||
arctic, // sky + teal + violet
|
||||
lapis, // blue + indigo + mint
|
||||
eggplant, // violet + purple + indigo
|
||||
lime, // lime + yellow + orange
|
||||
grim, // mauve + slate + sage
|
||||
}
|
||||
|
||||
enum _RadixBaseColor {
|
||||
tomato,
|
||||
red,
|
||||
crimson,
|
||||
pink,
|
||||
plum,
|
||||
purple,
|
||||
violet,
|
||||
indigo,
|
||||
blue,
|
||||
sky,
|
||||
cyan,
|
||||
teal,
|
||||
mint,
|
||||
green,
|
||||
grass,
|
||||
lime,
|
||||
yellow,
|
||||
amber,
|
||||
orange,
|
||||
brown,
|
||||
}
|
||||
|
||||
RadixColor _radixGraySteps(
|
||||
Brightness brightness, bool alpha, _RadixBaseColor baseColor) {
|
||||
switch (baseColor) {
|
||||
case _RadixBaseColor.tomato:
|
||||
case _RadixBaseColor.red:
|
||||
case _RadixBaseColor.crimson:
|
||||
case _RadixBaseColor.pink:
|
||||
case _RadixBaseColor.plum:
|
||||
case _RadixBaseColor.purple:
|
||||
case _RadixBaseColor.violet:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.mauveA
|
||||
: RadixColors.mauveA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.mauve
|
||||
: RadixColors.mauve);
|
||||
case _RadixBaseColor.indigo:
|
||||
case _RadixBaseColor.blue:
|
||||
case _RadixBaseColor.sky:
|
||||
case _RadixBaseColor.cyan:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.slateA
|
||||
: RadixColors.slateA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.slate
|
||||
: RadixColors.slate);
|
||||
case _RadixBaseColor.teal:
|
||||
case _RadixBaseColor.mint:
|
||||
case _RadixBaseColor.green:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.sageA
|
||||
: RadixColors.sageA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sage
|
||||
: RadixColors.sage);
|
||||
case _RadixBaseColor.lime:
|
||||
case _RadixBaseColor.grass:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.oliveA
|
||||
: RadixColors.oliveA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.olive
|
||||
: RadixColors.olive);
|
||||
case _RadixBaseColor.yellow:
|
||||
case _RadixBaseColor.amber:
|
||||
case _RadixBaseColor.orange:
|
||||
case _RadixBaseColor.brown:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.sandA
|
||||
: RadixColors.sandA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sand
|
||||
: RadixColors.sand);
|
||||
}
|
||||
}
|
||||
|
||||
RadixColor _radixColorSteps(
|
||||
Brightness brightness, bool alpha, _RadixBaseColor baseColor) {
|
||||
switch (baseColor) {
|
||||
case _RadixBaseColor.tomato:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.tomatoA
|
||||
: RadixColors.tomatoA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.tomato
|
||||
: RadixColors.tomato);
|
||||
case _RadixBaseColor.red:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.redA
|
||||
: RadixColors.redA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.red
|
||||
: RadixColors.red);
|
||||
case _RadixBaseColor.crimson:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.crimsonA
|
||||
: RadixColors.crimsonA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.crimson
|
||||
: RadixColors.crimson);
|
||||
case _RadixBaseColor.pink:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.pinkA
|
||||
: RadixColors.pinkA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.pink
|
||||
: RadixColors.pink);
|
||||
case _RadixBaseColor.plum:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.plumA
|
||||
: RadixColors.plumA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.plum
|
||||
: RadixColors.plum);
|
||||
case _RadixBaseColor.purple:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.purpleA
|
||||
: RadixColors.purpleA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.purple
|
||||
: RadixColors.purple);
|
||||
case _RadixBaseColor.violet:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.violetA
|
||||
: RadixColors.violetA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.violet
|
||||
: RadixColors.violet);
|
||||
case _RadixBaseColor.indigo:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.indigoA
|
||||
: RadixColors.indigoA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.indigo
|
||||
: RadixColors.indigo);
|
||||
case _RadixBaseColor.blue:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.blueA
|
||||
: RadixColors.blueA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.blue
|
||||
: RadixColors.blue);
|
||||
case _RadixBaseColor.sky:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.skyA
|
||||
: RadixColors.skyA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sky
|
||||
: RadixColors.sky);
|
||||
case _RadixBaseColor.cyan:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.cyanA
|
||||
: RadixColors.cyanA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.cyan
|
||||
: RadixColors.cyan);
|
||||
case _RadixBaseColor.teal:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.tealA
|
||||
: RadixColors.tealA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.teal
|
||||
: RadixColors.teal);
|
||||
case _RadixBaseColor.mint:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.mintA
|
||||
: RadixColors.mintA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.mint
|
||||
: RadixColors.mint);
|
||||
case _RadixBaseColor.green:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.greenA
|
||||
: RadixColors.greenA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.green
|
||||
: RadixColors.green);
|
||||
case _RadixBaseColor.grass:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.grassA
|
||||
: RadixColors.grassA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.grass
|
||||
: RadixColors.grass);
|
||||
case _RadixBaseColor.lime:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.limeA
|
||||
: RadixColors.limeA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.lime
|
||||
: RadixColors.lime);
|
||||
case _RadixBaseColor.yellow:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.yellowA
|
||||
: RadixColors.yellowA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.yellow
|
||||
: RadixColors.yellow);
|
||||
case _RadixBaseColor.amber:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.amberA
|
||||
: RadixColors.amberA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.amber
|
||||
: RadixColors.amber);
|
||||
case _RadixBaseColor.orange:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.orangeA
|
||||
: RadixColors.orangeA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.orange
|
||||
: RadixColors.orange);
|
||||
case _RadixBaseColor.brown:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.brownA
|
||||
: RadixColors.brownA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.brown
|
||||
: RadixColors.brown);
|
||||
}
|
||||
}
|
||||
|
||||
extension ToScaleColor on RadixColor {
|
||||
ScaleColor toScale() => ScaleColor(
|
||||
appBackground: step1,
|
||||
subtleBackground: step2,
|
||||
elementBackground: step3,
|
||||
hoverElementBackground: step4,
|
||||
activeElementBackground: step5,
|
||||
subtleBorder: step6,
|
||||
border: step7,
|
||||
hoverBorder: step8,
|
||||
background: step9,
|
||||
hoverBackground: step10,
|
||||
subtleText: step11,
|
||||
text: step12,
|
||||
);
|
||||
}
|
||||
|
||||
class RadixScheme {
|
||||
RadixScheme(
|
||||
{required this.primaryScale,
|
||||
required this.primaryAlphaScale,
|
||||
required this.secondaryScale,
|
||||
required this.tertiaryScale,
|
||||
required this.grayScale,
|
||||
required this.errorScale});
|
||||
|
||||
RadixColor primaryScale;
|
||||
RadixColor primaryAlphaScale;
|
||||
RadixColor secondaryScale;
|
||||
RadixColor tertiaryScale;
|
||||
RadixColor grayScale;
|
||||
RadixColor errorScale;
|
||||
|
||||
ScaleScheme toScale() => ScaleScheme(
|
||||
primaryScale: primaryScale.toScale(),
|
||||
primaryAlphaScale: primaryAlphaScale.toScale(),
|
||||
secondaryScale: secondaryScale.toScale(),
|
||||
tertiaryScale: tertiaryScale.toScale(),
|
||||
grayScale: grayScale.toScale(),
|
||||
errorScale: errorScale.toScale(),
|
||||
);
|
||||
}
|
||||
|
||||
RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) {
|
||||
late RadixScheme radixScheme;
|
||||
switch (themeColor) {
|
||||
// tomato + red + violet
|
||||
case RadixThemeColor.scarlet:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.red),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.tomato),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.yellow));
|
||||
|
||||
// crimson + purple + pink
|
||||
case RadixThemeColor.babydoll:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.crimson),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.crimson),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.pink),
|
||||
grayScale:
|
||||
_radixGraySteps(brightness, false, _RadixBaseColor.crimson),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange));
|
||||
// pink + cyan + plum
|
||||
case RadixThemeColor.vapor:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.pink),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.pink),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.cyan),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.plum),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.pink),
|
||||
errorScale: _radixColorSteps(brightness, false, _RadixBaseColor.red));
|
||||
// yellow + amber + orange
|
||||
case RadixThemeColor.gold:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.yellow),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.yellow),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.amber),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.yellow),
|
||||
errorScale: _radixColorSteps(brightness, false, _RadixBaseColor.red));
|
||||
// grass + orange + brown
|
||||
case RadixThemeColor.garden:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.grass),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.grass),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.brown),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.grass),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato));
|
||||
// green + brown + amber
|
||||
case RadixThemeColor.forest:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.green),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.green),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.brown),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.amber),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.green),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato));
|
||||
// sky + teal + violet
|
||||
case RadixThemeColor.arctic:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.sky),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.sky),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.teal),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.sky),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.crimson));
|
||||
// blue + indigo + mint
|
||||
case RadixThemeColor.lapis:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.blue),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.blue),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.mint),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.blue),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.crimson));
|
||||
// violet + purple + indigo
|
||||
case RadixThemeColor.eggplant:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.violet),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.violet),
|
||||
errorScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.crimson));
|
||||
// lime + yellow + orange
|
||||
case RadixThemeColor.lime:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.lime),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.lime),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.yellow),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.lime),
|
||||
errorScale: _radixColorSteps(brightness, false, _RadixBaseColor.red));
|
||||
// mauve + slate + sage
|
||||
case RadixThemeColor.grim:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixGraySteps(brightness, false, _RadixBaseColor.tomato),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.teal),
|
||||
grayScale: brightness == Brightness.dark
|
||||
? RadixColors.dark.gray
|
||||
: RadixColors.gray,
|
||||
errorScale: _radixColorSteps(brightness, false, _RadixBaseColor.red));
|
||||
}
|
||||
return radixScheme;
|
||||
}
|
||||
|
||||
ColorScheme _radixColorScheme(Brightness brightness, RadixScheme radix) =>
|
||||
ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: radix.primaryScale.step9,
|
||||
onPrimary: radix.primaryScale.step12,
|
||||
primaryContainer: radix.primaryScale.step4,
|
||||
onPrimaryContainer: radix.primaryScale.step11,
|
||||
secondary: radix.secondaryScale.step9,
|
||||
onSecondary: radix.secondaryScale.step12,
|
||||
secondaryContainer: radix.secondaryScale.step3,
|
||||
onSecondaryContainer: radix.secondaryScale.step11,
|
||||
tertiary: radix.tertiaryScale.step9,
|
||||
onTertiary: radix.tertiaryScale.step12,
|
||||
tertiaryContainer: radix.tertiaryScale.step3,
|
||||
onTertiaryContainer: radix.tertiaryScale.step11,
|
||||
error: radix.errorScale.step9,
|
||||
onError: radix.errorScale.step12,
|
||||
errorContainer: radix.errorScale.step3,
|
||||
onErrorContainer: radix.errorScale.step11,
|
||||
background: radix.grayScale.step1,
|
||||
onBackground: radix.grayScale.step11,
|
||||
surface: radix.primaryScale.step1,
|
||||
onSurface: radix.primaryScale.step12,
|
||||
surfaceVariant: radix.secondaryScale.step2,
|
||||
onSurfaceVariant: radix.secondaryScale.step11,
|
||||
outline: radix.primaryScale.step7,
|
||||
outlineVariant: radix.primaryScale.step6,
|
||||
shadow: RadixColors.dark.gray.step1,
|
||||
scrim: radix.primaryScale.step9,
|
||||
inverseSurface: radix.primaryScale.step11,
|
||||
onInverseSurface: radix.primaryScale.step2,
|
||||
inversePrimary: radix.primaryScale.step10,
|
||||
surfaceTint: radix.primaryAlphaScale.step4,
|
||||
);
|
||||
|
||||
ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
|
||||
DefaultChatTheme(
|
||||
primaryColor: scale.primaryScale.background,
|
||||
secondaryColor: scale.secondaryScale.background,
|
||||
backgroundColor: scale.grayScale.subtleBackground,
|
||||
inputBackgroundColor: Colors.blue,
|
||||
inputBorderRadius: BorderRadius.zero,
|
||||
inputTextDecoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: scale.primaryScale.elementBackground,
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||
),
|
||||
inputContainerDecoration: BoxDecoration(color: scale.primaryScale.border),
|
||||
inputPadding: const EdgeInsets.all(9),
|
||||
inputTextColor: scale.primaryScale.text,
|
||||
attachmentButtonIcon: const Icon(Icons.attach_file),
|
||||
);
|
||||
|
||||
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
|
||||
final textTheme = (brightness == Brightness.light)
|
||||
? Typography.blackCupertino
|
||||
: Typography.whiteCupertino;
|
||||
final radix = _radixScheme(brightness, themeColor);
|
||||
final colorScheme = _radixColorScheme(brightness, radix);
|
||||
final scaleScheme = radix.toScale();
|
||||
|
||||
final themeData = ThemeData.from(
|
||||
colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true);
|
||||
return themeData.copyWith(
|
||||
bottomSheetTheme: themeData.bottomSheetTheme.copyWith(
|
||||
elevation: 0,
|
||||
modalElevation: 0,
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
scaleScheme,
|
||||
]);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import 'dart:typed_data';
|
||||
import '../entities/local_account.dart';
|
||||
import '../local_accounts/local_account.dart';
|
||||
import '../veilid_init.dart';
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
|
||||
|
|
12
lib/tools/stack_trace.dart
Normal file
12
lib/tools/stack_trace.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
/// Rethrows [error] with a stacktrace that is the combination of [stackTrace]
|
||||
/// and [StackTrace.current].
|
||||
Never throwErrorWithCombinedStackTrace(Object error, StackTrace stackTrace) {
|
||||
final chain = Chain([
|
||||
Trace.current(),
|
||||
...Chain.forTrace(stackTrace).traces,
|
||||
]); // .foldFrames((frame) => frame.package == 'riverpod');
|
||||
|
||||
Error.throwWithStackTrace(error, chain.toTrace().vmTrace);
|
||||
}
|
|
@ -1,22 +1,33 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'loggy.dart';
|
||||
|
||||
class StateLogger extends ProviderObserver {
|
||||
/// [BlocObserver] for the VeilidChat application that
|
||||
/// observes all state changes.
|
||||
class StateLogger extends BlocObserver {
|
||||
/// {@macro counter_observer}
|
||||
const StateLogger();
|
||||
|
||||
@override
|
||||
void didUpdateProvider(
|
||||
ProviderBase<Object?> provider,
|
||||
Object? previousValue,
|
||||
Object? newValue,
|
||||
ProviderContainer container,
|
||||
) {
|
||||
log.debug('''
|
||||
{
|
||||
provider: ${provider.name ?? provider.runtimeType},
|
||||
oldValue: $previousValue,
|
||||
newValue: $newValue
|
||||
}
|
||||
''');
|
||||
super.didUpdateProvider(provider, previousValue, newValue, container);
|
||||
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
|
||||
super.onChange(bloc, change);
|
||||
log.debug('Change: ${bloc.runtimeType} $change');
|
||||
}
|
||||
|
||||
@override
|
||||
void onCreate(BlocBase<dynamic> bloc) {
|
||||
super.onCreate(bloc);
|
||||
log.debug('Create: ${bloc.runtimeType}');
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose(BlocBase<dynamic> bloc) {
|
||||
super.onClose(bloc);
|
||||
log.debug('Close: ${bloc.runtimeType}');
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
|
||||
super.onError(bloc, error, stackTrace);
|
||||
log.error('Error: ${bloc.runtimeType} $error\n$stackTrace');
|
||||
}
|
||||
}
|
||||
|
|
34
lib/tools/stream_listenable.dart
Normal file
34
lib/tools/stream_listenable.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'loggy.dart';
|
||||
|
||||
/// Converts a [Stream] into a [Listenable]
|
||||
///
|
||||
/// {@tool snippet}
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// StreamListenable(stream)
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
class StreamListenable extends ChangeNotifier {
|
||||
/// Creates a [StreamListenable].
|
||||
///
|
||||
/// Every time the [Stream] receives an event this [ChangeNotifier] will
|
||||
/// notify its listeners.
|
||||
StreamListenable(Stream<dynamic> stream) {
|
||||
notifyListeners();
|
||||
_subscription = stream.asBroadcastStream().listen((_) => notifyListeners());
|
||||
}
|
||||
|
||||
late final StreamSubscription<dynamic> _subscription;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
unawaited(_subscription.cancel().onError((error, stackTrace) =>
|
||||
log.error('StreamListenable cancel error: $error\n$stackTrace')));
|
||||
super.dispose();
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../entities/preferences.dart';
|
||||
import 'radix_generator.dart';
|
||||
|
||||
part 'theme_service.g.dart';
|
||||
|
||||
class ScaleColor {
|
||||
ScaleColor({
|
||||
required this.appBackground,
|
||||
required this.subtleBackground,
|
||||
required this.elementBackground,
|
||||
required this.hoverElementBackground,
|
||||
required this.activeElementBackground,
|
||||
required this.subtleBorder,
|
||||
required this.border,
|
||||
required this.hoverBorder,
|
||||
required this.background,
|
||||
required this.hoverBackground,
|
||||
required this.subtleText,
|
||||
required this.text,
|
||||
});
|
||||
|
||||
Color appBackground;
|
||||
Color subtleBackground;
|
||||
Color elementBackground;
|
||||
Color hoverElementBackground;
|
||||
Color activeElementBackground;
|
||||
Color subtleBorder;
|
||||
Color border;
|
||||
Color hoverBorder;
|
||||
Color background;
|
||||
Color hoverBackground;
|
||||
Color subtleText;
|
||||
Color text;
|
||||
|
||||
ScaleColor copyWith(
|
||||
{Color? appBackground,
|
||||
Color? subtleBackground,
|
||||
Color? elementBackground,
|
||||
Color? hoverElementBackground,
|
||||
Color? activeElementBackground,
|
||||
Color? subtleBorder,
|
||||
Color? border,
|
||||
Color? hoverBorder,
|
||||
Color? background,
|
||||
Color? hoverBackground,
|
||||
Color? subtleText,
|
||||
Color? text}) =>
|
||||
ScaleColor(
|
||||
appBackground: appBackground ?? this.appBackground,
|
||||
subtleBackground: subtleBackground ?? this.subtleBackground,
|
||||
elementBackground: elementBackground ?? this.elementBackground,
|
||||
hoverElementBackground:
|
||||
hoverElementBackground ?? this.hoverElementBackground,
|
||||
activeElementBackground:
|
||||
activeElementBackground ?? this.activeElementBackground,
|
||||
subtleBorder: subtleBorder ?? this.subtleBorder,
|
||||
border: border ?? this.border,
|
||||
hoverBorder: hoverBorder ?? this.hoverBorder,
|
||||
background: background ?? this.background,
|
||||
hoverBackground: hoverBackground ?? this.hoverBackground,
|
||||
subtleText: subtleText ?? this.subtleText,
|
||||
text: text ?? this.text,
|
||||
);
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static ScaleColor lerp(ScaleColor a, ScaleColor b, double t) => ScaleColor(
|
||||
appBackground: Color.lerp(a.appBackground, b.appBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
subtleBackground:
|
||||
Color.lerp(a.subtleBackground, b.subtleBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
elementBackground:
|
||||
Color.lerp(a.elementBackground, b.elementBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
hoverElementBackground:
|
||||
Color.lerp(a.hoverElementBackground, b.hoverElementBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
activeElementBackground: Color.lerp(
|
||||
a.activeElementBackground, b.activeElementBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
subtleBorder: Color.lerp(a.subtleBorder, b.subtleBorder, t) ??
|
||||
const Color(0x00000000),
|
||||
border: Color.lerp(a.border, b.border, t) ?? const Color(0x00000000),
|
||||
hoverBorder: Color.lerp(a.hoverBorder, b.hoverBorder, t) ??
|
||||
const Color(0x00000000),
|
||||
background: Color.lerp(a.background, b.background, t) ??
|
||||
const Color(0x00000000),
|
||||
hoverBackground: Color.lerp(a.hoverBackground, b.hoverBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
subtleText: Color.lerp(a.subtleText, b.subtleText, t) ??
|
||||
const Color(0x00000000),
|
||||
text: Color.lerp(a.text, b.text, t) ?? const Color(0x00000000),
|
||||
);
|
||||
}
|
||||
|
||||
class ScaleScheme extends ThemeExtension<ScaleScheme> {
|
||||
ScaleScheme(
|
||||
{required this.primaryScale,
|
||||
required this.primaryAlphaScale,
|
||||
required this.secondaryScale,
|
||||
required this.tertiaryScale,
|
||||
required this.grayScale,
|
||||
required this.errorScale});
|
||||
|
||||
final ScaleColor primaryScale;
|
||||
final ScaleColor primaryAlphaScale;
|
||||
final ScaleColor secondaryScale;
|
||||
final ScaleColor tertiaryScale;
|
||||
final ScaleColor grayScale;
|
||||
final ScaleColor errorScale;
|
||||
|
||||
@override
|
||||
ScaleScheme copyWith(
|
||||
{ScaleColor? primaryScale,
|
||||
ScaleColor? primaryAlphaScale,
|
||||
ScaleColor? secondaryScale,
|
||||
ScaleColor? tertiaryScale,
|
||||
ScaleColor? grayScale,
|
||||
ScaleColor? errorScale}) =>
|
||||
ScaleScheme(
|
||||
primaryScale: primaryScale ?? this.primaryScale,
|
||||
primaryAlphaScale: primaryAlphaScale ?? this.primaryAlphaScale,
|
||||
secondaryScale: secondaryScale ?? this.secondaryScale,
|
||||
tertiaryScale: tertiaryScale ?? this.tertiaryScale,
|
||||
grayScale: grayScale ?? this.grayScale,
|
||||
errorScale: errorScale ?? this.errorScale,
|
||||
);
|
||||
|
||||
@override
|
||||
ScaleScheme lerp(ScaleScheme? other, double t) {
|
||||
if (other is! ScaleScheme) {
|
||||
return this;
|
||||
}
|
||||
return ScaleScheme(
|
||||
primaryScale: ScaleColor.lerp(primaryScale, other.primaryScale, t),
|
||||
primaryAlphaScale:
|
||||
ScaleColor.lerp(primaryAlphaScale, other.primaryAlphaScale, t),
|
||||
secondaryScale: ScaleColor.lerp(secondaryScale, other.secondaryScale, t),
|
||||
tertiaryScale: ScaleColor.lerp(tertiaryScale, other.tertiaryScale, t),
|
||||
grayScale: ScaleColor.lerp(grayScale, other.grayScale, t),
|
||||
errorScale: ScaleColor.lerp(errorScale, other.errorScale, t),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ThemeService {
|
||||
ThemeService._();
|
||||
static late SharedPreferences prefs;
|
||||
static ThemeService? _instance;
|
||||
|
||||
static Future<ThemeService> get instance async {
|
||||
if (_instance == null) {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
_instance = ThemeService._();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static bool get isPlatformDark =>
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark;
|
||||
|
||||
ThemeData get initial {
|
||||
final themePreferences = load();
|
||||
return get(themePreferences);
|
||||
}
|
||||
|
||||
ThemePreferences load() {
|
||||
final themePreferencesJson = prefs.getString('themePreferences');
|
||||
ThemePreferences? themePreferences;
|
||||
if (themePreferencesJson != null) {
|
||||
try {
|
||||
themePreferences =
|
||||
ThemePreferences.fromJson(jsonDecode(themePreferencesJson));
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (_) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return themePreferences ??
|
||||
const ThemePreferences(
|
||||
colorPreference: ColorPreference.vapor,
|
||||
brightnessPreference: BrightnessPreference.system,
|
||||
displayScale: 1,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> save(ThemePreferences themePreferences) async {
|
||||
await prefs.setString(
|
||||
'themePreferences', jsonEncode(themePreferences.toJson()));
|
||||
}
|
||||
|
||||
ThemeData get(ThemePreferences themePreferences) {
|
||||
late final Brightness brightness;
|
||||
switch (themePreferences.brightnessPreference) {
|
||||
case BrightnessPreference.system:
|
||||
if (isPlatformDark) {
|
||||
brightness = Brightness.dark;
|
||||
} else {
|
||||
brightness = Brightness.light;
|
||||
}
|
||||
case BrightnessPreference.light:
|
||||
brightness = Brightness.light;
|
||||
case BrightnessPreference.dark:
|
||||
brightness = Brightness.dark;
|
||||
}
|
||||
|
||||
late final ThemeData themeData;
|
||||
switch (themePreferences.colorPreference) {
|
||||
// Special cases
|
||||
case ColorPreference.contrast:
|
||||
// xxx do contrastGenerator
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.grim);
|
||||
// Generate from Radix
|
||||
case ColorPreference.scarlet:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.scarlet);
|
||||
case ColorPreference.babydoll:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.babydoll);
|
||||
case ColorPreference.vapor:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.vapor);
|
||||
case ColorPreference.gold:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.gold);
|
||||
case ColorPreference.garden:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.garden);
|
||||
case ColorPreference.forest:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.forest);
|
||||
case ColorPreference.arctic:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.arctic);
|
||||
case ColorPreference.lapis:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.lapis);
|
||||
case ColorPreference.eggplant:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.eggplant);
|
||||
case ColorPreference.lime:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.lime);
|
||||
case ColorPreference.grim:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.grim);
|
||||
}
|
||||
|
||||
return themeData;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
FutureOr<ThemeService> themeService(ThemeServiceRef ref) async =>
|
||||
await ThemeService.instance;
|
|
@ -1,24 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'theme_service.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$themeServiceHash() => r'87dbacb9df4923f507fb01e486b91d73a3fcef9c';
|
||||
|
||||
/// See also [themeService].
|
||||
@ProviderFor(themeService)
|
||||
final themeServiceProvider = AutoDisposeFutureProvider<ThemeService>.internal(
|
||||
themeService,
|
||||
name: r'themeServiceProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$themeServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef ThemeServiceRef = AutoDisposeFutureProviderRef<ThemeService>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
|
@ -1,10 +1,10 @@
|
|||
export 'animations.dart';
|
||||
export 'async_table_db_backed_cubit.dart';
|
||||
export 'async_value.dart';
|
||||
export 'loggy.dart';
|
||||
export 'phono_byte.dart';
|
||||
export 'radix_generator.dart';
|
||||
export 'responsive.dart';
|
||||
export 'scanner_error_widget.dart';
|
||||
export 'secret_crypto.dart';
|
||||
export 'state_logger.dart';
|
||||
export 'theme_service.dart';
|
||||
export 'widget_helpers.dart';
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
|||
import 'package:motion_toast/motion_toast.dart';
|
||||
import 'package:quickalert/quickalert.dart';
|
||||
|
||||
import 'theme_service.dart';
|
||||
import '../theme/theme.dart';
|
||||
|
||||
extension BorderExt on Widget {
|
||||
DecoratedBox debugBorder() => DecoratedBox(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue