This commit is contained in:
Evan Reichard 2021-01-23 19:28:26 -05:00
parent fec7db1890
commit 4378a2927b
51 changed files with 992 additions and 245 deletions

View File

@ -34,11 +34,15 @@ PODS:
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- flutter_secure_storage (3.3.1):
- Flutter
- integration_test (0.0.1):
- Flutter
- SDWebImage (5.10.2):
- SDWebImage/Core (= 5.10.2)
- SDWebImage/Core (5.10.2)
- shared_preferences (0.0.1):
- Flutter
- SwiftyGif (5.3.0)
- url_launcher (0.0.1):
- Flutter
@ -46,7 +50,9 @@ PODS:
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
SPEC REPOS:
@ -61,8 +67,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
@ -71,8 +81,10 @@ SPEC CHECKSUMS:
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
integration_test: 5ed24a436eb7ec17b6a13046e9bf7ca4a404e59e
SDWebImage: b969dcfc02c40a5da71eac0b03b8f1a0c794a86f
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,68 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:imagini/bloc/bloc-prov.dart';
/// A Flutter [Widget] that merges multiple [BlocProvider] widgets into one widget tree.
///
/// [BlocProviderTree] improves the readability and eliminates the need
/// to nest multiple [BlocProviders].
///
/// By using [BlocProviderTree] we can go from:
///
/// ```dart
/// BlocProvider<BlocA>(
/// bloc: BlocA(),
/// child: BlocProvider<BlocB>(
/// bloc: BlocB(),
/// child: BlocProvider<BlocC>(
/// value: BlocC(),
/// child: ChildA(),
/// )
/// )
/// )
/// ```
///
/// to:
///
/// ```dart
/// BlocProviderTree(
/// blocProviders: [
/// BlocProvider<BlocA>(bloc: BlocA()),
/// BlocProvider<BlocB>(bloc: BlocB()),
/// BlocProvider<BlocC>(bloc: BlocC()),
/// ],
/// child: ChildA(),
/// )
/// ```
///
/// [BlocProviderTree] converts the [BlocProvider] list
/// into a tree of nested [BlocProvider] widgets.
/// As a result, the only advantage of using [BlocProviderTree] is improved
/// readability due to the reduction in nesting and boilerplate.
class BlocProviderTree extends StatelessWidget {
/// The [BlocProvider] list which is converted into a tree of [BlocProvider] widgets.
/// The tree of [BlocProvider] widgets is created in order meaning the first [BlocProvider]
/// will be the top-most [BlocProvider] and the last [BlocProvider] will be a direct ancestor
/// of the `child` [Widget].
final List<BlocProvider> blocProviders;
/// The [Widget] and its descendants which will have access to every [Bloc] provided by `blocProviders`.
/// This [Widget] will be a direct descendent of the last [BlocProvider] in `blocProviders`.
final Widget child;
const BlocProviderTree({
Key key,
@required this.blocProviders,
@required this.child,
}) : assert(blocProviders != null),
assert(child != null),
super(key: key);
@override
Widget build(BuildContext context) {
Widget tree = child;
for (final blocProvider in blocProviders.reversed) {
tree = blocProvider.copyWith(tree);
}
return tree;
}
}

View File

@ -1,60 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:imagini/bloc/bloc.dart';
/// A Flutter widget which provides a bloc to its children via `BlocProvider.of(context)`.
/// It is used as a DI widget so that a single instance of a bloc can be provided
/// to multiple widgets within a subtree.
class BlocProvider<T extends Bloc> extends InheritedWidget {
/// The [Bloc] which is to be made available throughout the subtree
final T bloc;
/// The [Widget] and its descendants which will have access to the [Bloc].
final Widget child;
BlocProvider({
Key key,
@required this.bloc,
this.child,
}) : assert(bloc != null),
super(key: key, child: child);
/// Method that allows widgets to access the bloc as long as their `BuildContext`
/// contains a `BlocProvider` instance.
static T of<T extends Bloc>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
final BlocProvider<T> provider = context
.ancestorInheritedElementForWidgetOfExactType(type)
?.widget as BlocProvider<T>;
if (provider == null) {
throw FlutterError(
"""
BlocProvider.of() called with a context that does not contain a Bloc of type $T.
No ancestor could be found starting from the context that was passed to BlocProvider.of<$T>().
This can happen if the context you use comes from a widget above the BlocProvider.
This can also happen if you used BlocProviderTree and didn\'t explicity provide
the BlocProvider types: BlocProvider(bloc: $T()) instead of BlocProvider<$T>(bloc: $T()).
The context used was: $context
""",
);
}
return provider?.bloc;
}
/// Clone the current [BlocProvider] with a new child [Widget].
/// All other values, including [Key] and [Bloc] are preserved.
BlocProvider<T> copyWith(Widget child) {
return BlocProvider<T>(
key: key,
bloc: bloc,
child: child,
);
}
/// Necessary to obtain generic [Type]
/// https://github.com/dart-lang/sdk/issues/11923
static Type _typeOf<T>() => T;
@override
bool updateShouldNotify(BlocProvider oldWidget) => false;
}

View File

@ -1 +0,0 @@
abstract class Bloc {}

View File

@ -1,5 +0,0 @@
import 'package:imagini/bloc/bloc.dart';
class AuthBloc extends Bloc {
AuthBloc();
}

View File

@ -1,2 +0,0 @@
export 'auth-bloc.dart';
export 'pref-bloc.dart';

View File

@ -0,0 +1,42 @@
import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:imagini/blocs/login/events.dart';
import 'package:imagini/blocs/login/states.dart';
import 'package:imagini/services/api/api.dart';
import 'package:imagini/services/api/exceptions.dart';
class LoginBloc extends Bloc<LoginEvents, LoginState> {
final ImaginiAPI imaginiAPI;
Map<String, String> loginResult;
String exampleResult;
LoginBloc({ this.imaginiAPI }) : super(LoginInitState());
@override
Stream<LoginState> mapEventToState(LoginEvents event) async* {
switch (event) {
case LoginEvents.loginResult:
yield LoginLoading();
try {
// exampleResult = await imaginiAPI.exampleApi();
loginResult = await imaginiAPI.loginAPI("admin", "admin");
yield LoginSuccess();
} on SocketException {
yield LoginFailed(
error: ConnectionRefusedException('No Internet'),
);
} on FormatException {
yield LoginFailed(
error: InvalidFormatException('Invalid Response Format'),
);
} catch (e) {
print(e);
yield LoginFailed(
error: UnknownException('Unknown Error'),
);
}
break;
}
}
}

View File

@ -0,0 +1,3 @@
enum LoginEvents {
loginResult,
}

View File

@ -0,0 +1,28 @@
// import 'package:equatable/equatable.dart';
//
// abstract class LoginState extends Equatable {
// @override
// List<Object> get props => [];
// }
abstract class LoginState {}
class LoginInitState extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}
class LoginNeeded extends LoginState {}
class LoginFailed extends LoginState {
final error;
LoginFailed({this.error});
}
class LoginLoaded extends LoginState {}
class LoginListError extends LoginState {
final error;
LoginListError({this.error});
}

View File

@ -1,5 +0,0 @@
import 'package:imagini/bloc/bloc.dart';
class PrefBloc extends Bloc {
PrefBloc();
}

View File

@ -0,0 +1,23 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:imagini/blocs/theme/events.dart';
import 'package:imagini/blocs/theme/state.dart';
import 'package:imagini/settings/app_themes.dart';
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
//
ThemeBloc()
: super(
ThemeState(
themeData: AppThemes.appThemeData[AppTheme.lightTheme],
),
);
@override
Stream<ThemeState> mapEventToState(ThemeEvent event) async* {
if (event is ThemeEvent) {
yield ThemeState(
themeData: AppThemes.appThemeData[event.appTheme],
);
}
}
}

View File

@ -0,0 +1,6 @@
import 'package:imagini/settings/app_themes.dart';
class ThemeEvent {
final AppTheme appTheme;
ThemeEvent({this.appTheme});
}

View File

@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
class ThemeState {
final ThemeData themeData;
ThemeState({this.themeData});
}

View File

@ -1,27 +1,37 @@
import 'package:flutter/material.dart';
import 'package:imagini/theme/style.dart';
import 'package:imagini/routes.dart';
import 'package:imagini/bloc/bloc-prov-tree.dart';
import 'package:imagini/bloc/bloc-prov.dart';
import 'package:imagini/blocs/blocs.dart';
import 'blocs/blocs.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(ExampleApp());
import 'package:imagini/services/api/api.dart';
import 'package:imagini/screens/login.dart';
import 'package:imagini/blocs/login/bloc.dart';
import 'package:imagini/settings/preferences.dart';
import 'package:imagini/blocs/theme/bloc.dart';
import 'package:imagini/blocs/theme/state.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Preferences.init();
runApp(ImaginiApp());
}
class ExampleApp extends StatelessWidget {
class ImaginiApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProviderTree(
blocProviders: <BlocProvider>[
BlocProvider<AuthBloc>(bloc: AuthBloc()),
BlocProvider<PrefBloc>(bloc: PrefBloc()),
],
child: MaterialApp(
title: 'ExampleApp',
theme: appTheme(),
initialRoute: '/',
routes: routes,
return BlocProvider(
create: (context) => ThemeBloc(),
child: BlocBuilder<ThemeBloc, ThemeState>(builder: (BuildContext context, ThemeState themeState) {
return MaterialApp(
title: 'Imagini',
debugShowCheckedModeBanner: false,
theme: themeState.themeData,
home: BlocProvider(
// create: (context) => LoginBloc(albumsRepo: AlbumServices()),
create: (context) => LoginBloc(imaginiAPI: ImaginiAPI()),
child: LoginScreen(),
),
);
},
),
);
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:imagini/blocs/login/bloc.dart';
import 'package:imagini/blocs/login/states.dart';
import 'package:imagini/blocs/login/events.dart';
import 'package:imagini/blocs/theme/bloc.dart';
import 'package:imagini/blocs/theme/events.dart';
import 'package:imagini/settings/preferences.dart';
import 'package:imagini/settings/app_themes.dart';
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
@override
void initState() {
super.initState();
_loadTheme();
_loadLogin();
}
_loadTheme() async {
context.read<ThemeBloc>().add(ThemeEvent(appTheme: Preferences.getTheme()));
}
_loadLogin() async {
context.read<LoginBloc>().add(LoginEvents.loginResult);
}
_setTheme(bool darkTheme) async {
AppTheme selectedTheme =
darkTheme ? AppTheme.lightTheme : AppTheme.darkTheme;
context.read<ThemeBloc>().add(ThemeEvent(appTheme: selectedTheme));
Preferences.saveTheme(selectedTheme);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _body(),
);
}
_body() {
return BlocBuilder<LoginBloc, LoginState>(builder: (BuildContext context, LoginState state) {
// Set Theme
_setTheme(true);
if (state is LoginNeeded) {
// TODO: Load Login Form
return Center( child: Text("Login Needed") );
}
if (state is LoginFailed) {
// TODO: Update Form Failed
return Center( child: Text("Login Failed: ${state.error.message.toString()}") );
}
if (state is LoginSuccess) {
// TODO: Navigate to /Gallery
return Center( child: Text("Login Success") );
}
// TODO: Login Screen
return Center( child: Text("Login Loading") );
});
}
}

View File

@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: () {
// Go to Login Screen
Navigator.pushNamed(context, '/Gallery');
},
child: Text('Login'),
),
);
}
}

View File

@ -31,7 +31,7 @@ class _GalleryScreenState extends State<GalleryScreen> {
bloc: exampleBloc,
child: PlatformScaffold(
appBar: PlatformAppBar(
title: Text('Gallerys'),
title: Text('Gallery'),
cupertino: (_, __) => CupertinoNavigationBarData(
// Issue with cupertino where a bar with no transparency
// will push the list down. Adding some alpha value fixes it (in a hacky way)

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.fromLTRB(50, 0, 50, 0),
height: 500,
child: Column(
children: <Widget>[
Container(
child: FittedBox(
fit: BoxFit.contain,
child: const FlutterLogo(),
),
width: 175,
margin: EdgeInsets.fromLTRB(0, 0, 0, 50),
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: 'Server Address'
),
),
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: 'Username / Email'
),
),
),
Expanded(
child: TextField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
labelText: 'Password'
),
),
),
SizedBox(
width: double.infinity,
height: 50,
child: RaisedButton(
onPressed: () {
Navigator.pushNamed(context, '/Gallery');
},
child: Text('Login')
),
),
],
),
),
);
}
}

View File

@ -28,9 +28,9 @@ class _LoginScreenState extends State<LoginScreen> {
return BlocProvider(
bloc: LoginBloc(),
child: Scaffold(
appBar: AppBar(
title: Text("Login"),
),
// appBar: AppBar(
// title: Text("Login"),
// ),
body: Body(),
),
);

View File

@ -1,38 +0,0 @@
import 'package:flutter/material.dart';
import 'package:imagini/screens/splash/components/body.dart';
import 'package:imagini/screens/splash/splash-bloc.dart';
import 'package:imagini/bloc/bloc-prov.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
SplashBloc splashBloc;
@override
void initState() {
super.initState();
splashBloc = SplashBloc();
}
@override
void dispose() {
splashBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocProvider(
bloc: SplashBloc(),
child: Scaffold(
appBar: AppBar(
title: Text("First Screen"),
),
body: Body(),
),
);
}
}

View File

@ -0,0 +1,85 @@
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
// import 'package:flutter/foundation.dart' show kIsWeb;
// import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// if(kIsWeb) {
// // Use web storage
// } else {
// // Use flutter_secure_storage
// }
// final _storage = FlutterSecureStorage();
class ImaginiAPI {
static const _baseUrl = "http://10.0.20.170:8484";
// static const String _GET_ALBUMS = '/albums';
Future<String> exampleApi() async {
http.Response response = await http.get(
Uri.encodeFull("https://www.example.com/api"),
);
print("Status: ${response.statusCode.toString()}");
print("Respone: ${response.body.toString()}");
return response.body;
}
Future<Map<String, String>> loginAPI(String user, String password) async {
http.Response response = await http.post(
Uri.encodeFull(_baseUrl + "/api/v1/Login"),
body: jsonEncode(<String, String>{
'user': user,
'password': password,
}),
).timeout(Duration(seconds: 10));
// TODO:
// - StatusCode:
// - 405 (StatusMethodNotAllowed)
// - 400 (StatusBadRequest)
// - 401 (StatusUnauthorized)
// -
// -
// -
String setCookieVal = response.headers["set-cookie"];
List<Cookie> allCookies = setCookieVal.split(',')
.map((cookie) => Cookie.fromSetCookieValue(cookie)).toList();
Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken");
Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken");
print("Status: ${response.statusCode.toString()}");
print("Body: ${response.body.toString()}");
print("AccessToken: ${accessToken.toString()}");
print("RefreshToken: ${refreshToken.toString()}");
return response.headers;
}
Future<String> mediaItemsAPI(String albumID, String tagID) async {
return null;
}
Future<String> tagsAPI() async {
return null;
}
Future<String> albumsAPI() async {
return null;
}
Future<String> meAPI() async {
return null;
}
// API Calls:
// - Login
// - MediaItems
// - Tags
// - Albums
// - Me
}

View File

@ -0,0 +1,19 @@
class ConnectionRefusedException {
var message;
ConnectionRefusedException(this.message);
}
class NoServiceFoundException {
var message;
NoServiceFoundException(this.message);
}
class InvalidFormatException {
var message;
InvalidFormatException(this.message);
}
class UnknownException {
var message;
UnknownException(this.message);
}

View File

@ -1,11 +0,0 @@
import 'dart:async';
import 'package:http/http.dart' as http;
Future<String> exampleApi(String orgid) async {
http.Response response = await http.get(
Uri.encodeFull("https://www.example.com/api"),
);
print("Respone ${response.body.toString()}");
//Returns 'true' or 'false' as a String
return response.body;
}

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class AppThemes {
static final appThemeData = {
AppTheme.lightTheme: ThemeData(
scaffoldBackgroundColor: Colors.white,
primarySwatch: Colors.blue,
backgroundColor: Colors.white,
textTheme: TextTheme(
bodyText1: TextStyle(
color: Colors.black,
),
),
),
AppTheme.darkTheme: ThemeData(
scaffoldBackgroundColor: Colors.black,
primarySwatch: Colors.teal,
backgroundColor: Colors.black,
textTheme: TextTheme(
bodyText1: TextStyle(
color: Colors.white,
),
),
)
};
}
enum AppTheme {
lightTheme,
darkTheme,
}

View File

@ -0,0 +1,39 @@
import 'dart:convert';
import 'package:imagini/settings/app_themes.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Preferences {
//
static SharedPreferences preferences;
static const String KEY_SELECTED_THEME = 'key_selected_theme';
static init() async {
preferences = await SharedPreferences.getInstance();
}
static void saveTheme(AppTheme selectedTheme) async {
if (null == selectedTheme) {
selectedTheme = AppTheme.lightTheme;
}
String theme = jsonEncode(selectedTheme.toString());
preferences.setString(KEY_SELECTED_THEME, theme);
}
static AppTheme getTheme() {
String theme = preferences.getString(KEY_SELECTED_THEME);
if (null == theme) {
return AppTheme.lightTheme;
}
return getThemeFromString(jsonDecode(theme));
}
static AppTheme getThemeFromString(String themeString) {
for (AppTheme theme in AppTheme.values) {
if (theme.toString() == themeString) {
return theme;
}
}
return null;
}
}

View File

@ -36,6 +36,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.3"
bloc:
dependency: transitive
description:
name: bloc
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.1"
boolean_selector:
dependency: transitive
description:
@ -92,13 +99,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
cross_file:
dependency: transitive
description:
name: cross_file
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
crypto:
dependency: transitive
description:
@ -113,6 +113,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
equatable:
dependency: "direct main"
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.5"
fake_async:
dependency: transitive
description:
@ -120,6 +127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.3"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file:
dependency: transitive
description:
@ -139,6 +153,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_bloc:
dependency: "direct main"
description:
name: flutter_bloc
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.1"
flutter_driver:
dependency: transitive
description: flutter
@ -158,6 +179,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.11"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.5"
flutter_test:
dependency: "direct dev"
description: flutter
@ -181,7 +209,7 @@ packages:
source: hosted
version: "1.2.0"
http:
dependency: transitive
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
@ -241,6 +269,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.6"
nested:
dependency: transitive
description:
name: nested
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
node_interop:
dependency: transitive
description:
@ -269,6 +304,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.3"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+3"
pedantic:
dependency: transitive
description:
@ -304,6 +360,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-nullsafety.4"
provider:
dependency: transitive
description:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.3"
pub_semver:
dependency: transitive
description:
@ -311,6 +374,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.12+4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+4"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+11"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+2"
sky_engine:
dependency: transitive
description: flutter
@ -386,13 +491,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.9"
tus_client:
dependency: "direct main"
description:
name: tus_client
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1"
typed_data:
dependency: transitive
description:
@ -477,6 +575,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.4"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
yaml:
dependency: transitive
description:

View File

@ -23,8 +23,12 @@ environment:
dependencies:
flutter:
sdk: flutter
equatable: ^1.2.5
http: ^0.12.2
shared_preferences: ^0.5.12+4
flutter_secure_storage: ^3.3.5
flutter_platform_widgets: ^0.72.0
tus_client: ^0.1.1
flutter_bloc: ^6.1.1
file_picker: ^2.1.5
url_launcher: ^5.7.10
cupertino_icons: ^1.0.1