diff --git a/lib/common/value_notifiers.dart b/lib/common/value_notifiers.dart index a59c88e..ae12fd0 100644 --- a/lib/common/value_notifiers.dart +++ b/lib/common/value_notifiers.dart @@ -1,10 +1,14 @@ +// Flutter imports: import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +// Project imports: import '../model/position.dart'; ValueNotifier foundText = ValueNotifier("searching"); +ValueNotifier hasStarted = ValueNotifier(false); +ValueNotifier lastRecorded = ValueNotifier(null); ValueNotifier counter = ValueNotifier(0); ValueNotifier position = ValueNotifier( diff --git a/lib/functions/ball_position.dart b/lib/functions/ball_position.dart index 232d199..91e006e 100644 --- a/lib/functions/ball_position.dart +++ b/lib/functions/ball_position.dart @@ -1,6 +1,8 @@ +// Dart imports: import 'dart:math'; import 'dart:ui'; +// Project imports: import '../model/position.dart'; Position getBallPosition({Size size, double ballSize}) { diff --git a/lib/functions/face_detector.dart b/lib/functions/face_detector.dart index edc7cac..753b55a 100644 --- a/lib/functions/face_detector.dart +++ b/lib/functions/face_detector.dart @@ -1,12 +1,15 @@ +// Dart imports: import 'dart:ui'; +// Package imports: import 'package:camera/camera.dart'; import 'package:firebase_ml_vision/firebase_ml_vision.dart'; +// Project imports: import '../common/value_notifiers.dart'; import '../model/data.dart'; -Future foundImage(CameraImage image) async { +Future foundImage(CameraImage image) async { final FirebaseVisionImageMetadata metadata = FirebaseVisionImageMetadata( rawFormat: image.format.raw, size: Size( @@ -43,9 +46,12 @@ Future foundImage(CameraImage image) async { if (faces.length > 0) { Offset nosePosition = faces[0].getLandmark(FaceLandmarkType.noseBase).position; - // The nose is in the viscinity of the dot and we record the time - if (((nosePosition.dx - position.value.x).abs() <= 250) && - ((nosePosition.dy - position.value.y).abs() <= 250)) { + + /// The [nose] is in the `viscinity of the dot` and we record the time + + int noticeRadius = 200; + if (((nosePosition.dx - position.value.x).abs() <= noticeRadius) && + ((nosePosition.dy - position.value.y).abs() <= noticeRadius)) { Duration duration = position.value.time.difference(DateTime.now()); if ((counter.value - 1) >= 0 && data[counter.value - 1] == 0) data[counter.value - 1] = duration.inMilliseconds.abs().floorToDouble(); @@ -53,6 +59,9 @@ Future foundImage(CameraImage image) async { if (counter.value > 15) foundText.value = "Your latency is $avg ms."; } return nosePosition; - } else + } else { foundText.value = "No one is there."; + if ((counter.value - 1) >= 0 && data[counter.value - 1] == 0) + data[counter.value - 1] = -1; + } } diff --git a/lib/main.dart b/lib/main.dart index 0defd74..395a699 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,24 +1,39 @@ +// Dart imports: import 'dart:async'; -import 'package:camera/camera.dart'; +// Flutter imports: import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +// Package imports: +import 'package:camera/camera.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_statusbar_manager/flutter_statusbar_manager.dart'; + +// Project imports: import 'view/home_screen.dart'; +class MyBehavior extends ScrollBehavior { + @override + Widget buildViewportChrome( + BuildContext context, Widget child, AxisDirection axisDirection) { + return child; + } +} + List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); cameras = await availableCameras(); - SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle( - statusBarColor: Colors.white, - statusBarIconBrightness: Brightness.light, - statusBarBrightness: Brightness.light, - systemNavigationBarColor: Colors.white, - systemNavigationBarIconBrightness: Brightness.dark, - ), + await FlutterStatusbarManager.setStyle(StatusBarStyle.DARK_CONTENT); + await FlutterStatusbarManager.setColor( + Colors.transparent, + animated: true, + ); + await FlutterStatusbarManager.setNavigationBarColor( + Colors.transparent, + animated: true, ); runApp(MyApp()); } @@ -26,17 +41,48 @@ Future main() async { class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData( - primaryColor: Colors.black, - accentColor: Color(0xffF92B5C), - backgroundColor: Colors.white, - visualDensity: VisualDensity.adaptivePlatformDensity, - fontFamily: 'metropolis', - brightness: Brightness.light, + return ScreenUtilInit( + designSize: Size(1080, 1920), + allowFontScaling: true, + builder: () => MaterialApp( + theme: ThemeData( + primaryColor: Colors.black, + accentColor: Color(0xffF92B5C), + backgroundColor: Colors.white, + visualDensity: VisualDensity.adaptivePlatformDensity, + fontFamily: 'metropolis', + brightness: Brightness.light, + textButtonTheme: TextButtonThemeData( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.black), + foregroundColor: MaterialStateProperty.all(Colors.white), + padding: MaterialStateProperty.all( + EdgeInsets.symmetric(vertical: 15, horizontal: 25), + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + alignment: Alignment.center, + enableFeedback: true, + elevation: MaterialStateProperty.all(20), + shadowColor: MaterialStateProperty.all(Colors.black12), + ), + ), + ), + home: Home(cameras: cameras), + debugShowCheckedModeBanner: false, + builder: (context, child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 0.9), + child: ScrollConfiguration( + behavior: MyBehavior(), + child: child, + ), + ); + }, ), - home: Home(cameras: cameras), - debugShowCheckedModeBanner: false, ); } } diff --git a/lib/view/home_screen.dart b/lib/view/home_screen.dart index e7da219..74f3517 100644 --- a/lib/view/home_screen.dart +++ b/lib/view/home_screen.dart @@ -1,11 +1,14 @@ +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: import 'package:auto_size_text/auto_size_text.dart'; -import 'package:boxicons_flutter/boxicons_flutter.dart'; import 'package:camera/camera.dart'; -import '../model/data.dart'; -import 'package:flutter/material.dart'; -import 'package:share/share.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +// Project imports: import '../common/value_notifiers.dart'; +import 'widgets/appbar.dart'; import 'widgets/camera.dart'; class Home extends StatelessWidget { @@ -16,102 +19,60 @@ class Home extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: SafeArea( - child: Container( - padding: EdgeInsets.fromLTRB( - 40, - 30, - 40, - 40, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyAppBar(), - SizedBox(height: 5), - AutoSizeText( - "Focus your nose on the crosshair", - style: TextStyle( - color: Theme.of(context).accentColor, - fontFamily: "metropolis", - fontSize: 20, - fontWeight: FontWeight.w700, - height: 1.3, - ), - maxLines: 1, - textAlign: TextAlign.start, - ), - AutoSizeText( - "and then follow the green dot with your head.", - style: TextStyle( - color: Colors.black, - fontFamily: "metropolis", - fontSize: 20, - fontWeight: FontWeight.w500, - height: 1.1, - ), - textAlign: TextAlign.start, - maxLines: 1, - ), - SizedBox(height: 5), - CameraWidget(cameras: cameras), - SizedBox(height: 5), - ValueListenableBuilder( - valueListenable: foundText, - builder: (context, value, w) { - return Text( - value, + child: SingleChildScrollView( + physics: BouncingScrollPhysics(), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 80.h, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(height: 80.h), + MyAppBar(), + AutoSizeText.rich( + TextSpan( + text: "Focus your nose on the crosshair\n", style: TextStyle( - fontSize: 15, + color: Theme.of(context).accentColor, + fontFamily: "metropolis", + fontSize: 20, + fontWeight: FontWeight.w700, + height: 1.5, ), - ); - }, - ), - ], - ), - ), - ), - ); - } -} - -class MyAppBar extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - RichText( - text: TextSpan( - text: "Dizzy\n", - style: TextStyle( - color: Colors.black, - fontFamily: "metropolis", - fontWeight: FontWeight.w400, - fontSize: 40, - height: 1.1, - ), - children: [ - TextSpan( - text: "Check", - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 45, + children: [ + TextSpan( + text: "and then follow the green dot with your head.", + style: TextStyle( + color: Colors.black, + fontFamily: "metropolis", + fontSize: 17, + fontWeight: FontWeight.w500, + height: 1.3, + ), + ), + ], ), ), + SizedBox(height: 60.h), + CameraWidget(cameras: cameras), + SizedBox(height: 60.h), + ValueListenableBuilder( + valueListenable: foundText, + builder: (context, value, w) { + return Text( + "Debug text: $value", + style: TextStyle( + fontSize: 15, + ), + ); + }, + ), + SizedBox(height: 80.h), ], ), ), - IconButton( - icon: Icon( - Boxicons.bxShareAlt, - size: 30, - ), - onPressed: () { - Share.share('My latency is $avg ms.'); - }, - ), - ], + ), ), ); } diff --git a/lib/view/widgets/appbar.dart b/lib/view/widgets/appbar.dart new file mode 100644 index 0000000..8b4af0f --- /dev/null +++ b/lib/view/widgets/appbar.dart @@ -0,0 +1,54 @@ +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: +import 'package:boxicons_flutter/boxicons_flutter.dart'; +import 'package:share/share.dart'; + +// Project imports: +import '../../model/data.dart'; + +class MyAppBar extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan( + text: "Dizzy\n", + style: TextStyle( + color: Colors.black, + fontFamily: "metropolis", + fontWeight: FontWeight.w400, + fontSize: 40, + height: 1.1, + ), + children: [ + TextSpan( + text: "Check", + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 45, + ), + ), + ], + ), + ), + IconButton( + icon: Icon( + Boxicons.bxShareAlt, + size: 30, + ), + iconSize: 30, + padding: EdgeInsets.all(12), + color: Colors.black, + onPressed: () { + Share.share('My latency is $avg ms.'); + }, + ), + ], + ); + } +} diff --git a/lib/view/widgets/bottomsheet.dart b/lib/view/widgets/bottomsheet.dart new file mode 100644 index 0000000..154a9c1 --- /dev/null +++ b/lib/view/widgets/bottomsheet.dart @@ -0,0 +1,77 @@ +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:share/share.dart'; + +// Project imports: +import '../../model/data.dart'; + +class ResultSheet extends StatelessWidget { + const ResultSheet({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(30), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "${avg < 0 ? "Failed test, face went out of viewport one or more than one times." : "Here is your last latency: $avg ms."}\n\n${avg > 50 || avg < 0 ? "Sorry not fit to drive" : "Engine on! You are fit to drive."}", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + height: 1.2, + ), + ), + SizedBox(height: 30), + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: AutoSizeText( + "Alright!", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + maxLines: 1, + ), + ), + SizedBox(width: 30), + TextButton( + onPressed: () { + Share.share('My latency is $avg ms.'); + }, + child: AutoSizeText( + "Share", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + maxLines: 1, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/widgets/camera.dart b/lib/view/widgets/camera.dart index 9a68bba..dcc52d3 100644 --- a/lib/view/widgets/camera.dart +++ b/lib/view/widgets/camera.dart @@ -1,16 +1,23 @@ +// Dart imports: import 'dart:async'; import 'dart:ui'; +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: import 'package:camera/camera.dart'; import 'package:dotted_line/dotted_line.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:logger/logger.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; +// Project imports: import '../../common/value_notifiers.dart'; import '../../functions/ball_position.dart'; import '../../functions/face_detector.dart'; import '../../model/data.dart'; +import 'bottomsheet.dart'; class CameraWidget extends StatefulWidget { final List cameras; @@ -25,6 +32,96 @@ class _CameraWidgetState extends State { GlobalKey _key = GlobalKey(); Offset nosePosition; + void startFunction() { + try { + hasStarted.value = true; + hasStarted.notifyListeners(); + data = List.generate(16, (index) => 0); + avg = 0; + Logger().v(controller.value.isStreamingImages); + controller.startImageStream((image) async { + nosePosition = await foundImage(image); + }).then((value) { + Logger().v("now: " + controller.value.isStreamingImages.toString()); + }); + timer = Timer.periodic(Duration(milliseconds: 800), (timer) async { + try { + final RenderBox renderBox = _key.currentContext.findRenderObject(); + Size size = renderBox.size; + position.value = getBallPosition(size: size, ballSize: 30); + counter.value++; + if (counter.value > 15) { + controller.stopImageStream(); + Logger().i("counter: " + counter.value.toString()); + int len = 0; + + /// [zeroCount] counts times when user `never` reaches the [dot] + int zeroCount = 0; + for (var i = 1; i < data.length; i++) { + double element = data[i]; + if (element > 0) { + avg += element; + len++; + } else if (element < 0) { + avg = -1; + break; + } else { + zeroCount++; + } + } + if (zeroCount > 3) avg = -1; + avg = avg > 0 ? (avg / len).floorToDouble() : avg; + Logger().i(data); + Logger().i("Average: $avg"); + timer.cancel(); + counter.value = 0; + foundText.value = + avg > 0 ? "Here is your latency: $avg ms." : "Failed test."; + hasStarted.value = false; + hasStarted.notifyListeners(); + await showModalBottomSheet( + context: context, + builder: (context) => ResultSheet(), + ); + } + } catch (e) { + Logger().e(e); + } finally { + setState(() {}); + } + }); + } catch (e) { + Logger().e(e); + } + } + + void stopFunction() { + try { + Logger().v(controller.value.isStreamingImages); + timer.cancel(); + controller.stopImageStream().then((value) { + Logger().v("now: " + controller.value.isStreamingImages.toString()); + }); + int len = 0; + data.forEach((element) { + if (element > 0) { + avg += element; + len++; + } + }); + avg = (avg / len).floorToDouble(); + Logger().i(data); + Logger().i("Average: $avg"); + counter.value = 0; + hasStarted.value = false; + hasStarted.notifyListeners(); + } catch (e) { + Logger().e(e); + } finally { + setState(() {}); + } + } + @override void initState() { super.initState(); @@ -55,189 +152,127 @@ class _CameraWidgetState extends State { @override Widget build(BuildContext context) { - if (!controller.value.isInitialized) { - return CircularProgressIndicator(); - } - return Expanded( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.grey[100], - offset: Offset(0, 5), - blurRadius: 15, - ), - ], - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Stack( - alignment: Alignment.center, - key: _key, - children: [ - controller.value.hasError == false - ? CameraPreview( - controller, - ) - : Text(controller.value.errorDescription.toString()), - DottedLine( - direction: Axis.horizontal, - lineLength: 100, - lineThickness: 2.0, - dashLength: 4.0, - dashColor: Theme.of(context).accentColor, - dashRadius: 10, - dashGapLength: 4.0, - dashGapColor: Colors.transparent, - dashGapRadius: 0.0, - ), - DottedLine( - direction: Axis.vertical, - lineLength: 100, - lineThickness: 2.0, - dashLength: 4.0, - dashColor: Theme.of(context).accentColor, - dashRadius: 10, - dashGapLength: 4.0, - dashGapColor: Colors.transparent, - dashGapRadius: 0.0, - ), - ValueListenableBuilder( - valueListenable: position, - builder: (context, value, _) { - return AnimatedPositioned( - duration: Duration( - milliseconds: 500, - ), - curve: Curves.easeOut, - top: value.y, - left: value.x, - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - color: Color(0xff33ff00), - shape: BoxShape.circle, - border: Border.all( - color: Colors.lightGreenAccent, - width: 5, - ), - ), - ), - ); - }, - ) - ], - ), - ), - SizedBox(height: 3), - ValueListenableBuilder( - valueListenable: counter, - builder: (context, value, _) { - return StepProgressIndicator( - totalSteps: 15, - padding: 0, - roundedEdges: Radius.circular(20), - currentStep: value, - selectedColor: Color(0xff33ff00), - unselectedColor: Colors.grey[350], - ); - }, - ), - SizedBox(height: 3), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.grey[100], + offset: Offset(0, 5), + blurRadius: 15, + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Stack( + alignment: Alignment.center, + key: _key, children: [ - MaterialButton( - onPressed: () { - try { - data = List.generate(16, (index) => 0); - avg = 0; - Logger().v(controller.value.isStreamingImages); - controller.startImageStream((image) async { - nosePosition = await foundImage(image); - }).then((value) { - Logger().v("now: " + - controller.value.isStreamingImages.toString()); - }); - timer = - Timer.periodic(Duration(milliseconds: 800), (timer) { - try { - final RenderBox renderBox = - _key.currentContext.findRenderObject(); - Size size = renderBox.size; - position.value = - getBallPosition(size: size, ballSize: 30); - counter.value++; - if (counter.value > 15) { - controller.stopImageStream(); - Logger().i("counter: " + counter.value.toString()); - int len = 0; - data.forEach((element) { - if (element > 0) { - avg += element; - len++; - } - }); - avg = (avg / len).floorToDouble(); - Logger().i(data); - Logger().i("Average: $avg"); - timer.cancel(); - counter.value = 0; - foundText.value = "Here is your latency: $avg ms."; - } - } catch (e) { - Logger().e(e); - } - }); - } catch (e) { - Logger().e(e); - } - }, - child: Text("Start"), - color: Theme.of(context).accentColor, - textColor: Colors.white, + controller.value.hasError == false + ? CameraPreview( + controller, + ) + : Text(controller.value.errorDescription.toString()), + DottedLine( + direction: Axis.horizontal, + lineLength: 100, + lineThickness: 2.0, + dashLength: 4.0, + dashColor: Theme.of(context).accentColor, + dashRadius: 10, + dashGapLength: 4.0, + dashGapColor: Colors.transparent, + dashGapRadius: 0.0, + ), + DottedLine( + direction: Axis.vertical, + lineLength: 100, + lineThickness: 2.0, + dashLength: 4.0, + dashColor: Theme.of(context).accentColor, + dashRadius: 10, + dashGapLength: 4.0, + dashGapColor: Colors.transparent, + dashGapRadius: 0.0, ), - MaterialButton( - onPressed: () { - try { - Logger().v(controller.value.isStreamingImages); - timer.cancel(); - controller.stopImageStream().then((value) { - Logger().v("now: " + - controller.value.isStreamingImages.toString()); - }); - int len = 0; - data.forEach((element) { - if (element > 0) { - avg += element; - len++; - } - }); - avg = (avg / len).floorToDouble(); - Logger().i(data); - Logger().i("Average: $avg"); - counter.value = 0; - } catch (e) { - Logger().e(e); - } finally { - setState(() {}); - } + ValueListenableBuilder( + valueListenable: position, + builder: (context, value, _) { + return AnimatedPositioned( + duration: Duration( + milliseconds: 500, + ), + curve: Curves.easeOut, + top: value.y, + left: value.x, + child: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + color: Color(0xff33ff00), + shape: BoxShape.circle, + border: Border.all( + color: Colors.lightGreenAccent, + width: 5, + ), + ), + ), + ); }, - child: Text("stop"), - color: Colors.black, - textColor: Colors.white, ) ], ), - ], - ), + ), + SizedBox(height: 40.h), + ValueListenableBuilder( + valueListenable: counter, + builder: (context, value, _) { + return StepProgressIndicator( + totalSteps: 15, + padding: 0, + roundedEdges: Radius.circular(20), + currentStep: value, + selectedColor: Color(0xff33ff00), + unselectedColor: Colors.grey[350], + ); + }, + ), + SizedBox(height: 20.h), + ConstrainedBox( + constraints: BoxConstraints( + minWidth: double.infinity, + ), + child: ValueListenableBuilder( + valueListenable: hasStarted, + builder: (context, value, child) => TextButton( + onPressed: value ? stopFunction : startFunction, + style: value + ? Theme.of(context).textButtonTheme.style + : Theme.of(context).textButtonTheme.style.copyWith( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).accentColor, + ), + ), + child: Text( + value ? "Stop" : "Start", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + ), + ), + ), + ), + ], ), ); } } + +// value ? stopFunction : startFunction diff --git a/pubspec.lock b/pubspec.lock index 30518d1..1acad71 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,13 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" auto_size_text: dependency: "direct main" description: @@ -21,7 +28,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" boxicons_flutter: dependency: "direct main" description: @@ -49,28 +56,28 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" cross_file: dependency: transitive description: @@ -98,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" firebase_core: dependency: "direct main" description: @@ -132,6 +139,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_screenutil: + dependency: "direct main" + description: + name: flutter_screenutil + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.4" + flutter_statusbar_manager: + dependency: "direct main" + description: + name: flutter_statusbar_manager + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -156,13 +177,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" + import_sorter: + dependency: "direct dev" + description: + name: import_sorter + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.2" js: dependency: transitive description: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3" logger: dependency: "direct main" description: @@ -176,14 +204,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" mime: dependency: transitive description: @@ -197,7 +225,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" pedantic: dependency: transitive description: @@ -237,14 +265,14 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" step_progress_indicator: dependency: "direct main" description: @@ -258,7 +286,7 @@ packages: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" stream_transform: dependency: transitive description: @@ -272,35 +300,49 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" + tint: + dependency: transitive + description: + name: tint + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.22.0 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3a5fac2..adb6100 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,12 +14,15 @@ dependencies: dotted_line: ^2.0.2 firebase_core: ^0.7.0 firebase_ml_vision: ^0.10.0 + flutter_screenutil: ^4.0.4 + flutter_statusbar_manager: ^2.0.0 logger: ^0.9.4 share: ^0.6.5+4 step_progress_indicator: ^0.2.5+8 dev_dependencies: flutter_test: sdk: flutter + import_sorter: ^4.4.2 flutter: uses-material-design: true fonts: