diff --git a/src/flutter/lib/Components/BG.dart b/src/flutter/lib/Components/BG.dart new file mode 100644 index 0000000..72a338a --- /dev/null +++ b/src/flutter/lib/Components/BG.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class BG extends StatelessWidget { + BG({@required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + child: child, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF6FABFF), Color(0xFFE4FF71)], + ), + ), + ), + ); + } +} diff --git a/src/flutter/lib/Components/Button.dart b/src/flutter/lib/Components/Button.dart new file mode 100644 index 0000000..987000c --- /dev/null +++ b/src/flutter/lib/Components/Button.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class Button extends StatelessWidget { + Button({@required this.text, @required this.callback}); + + final String text; + final Function callback; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: this.callback, + child: Container( + width: 180.0, + height: 45.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(100.0), + border: Border.all(width: 1.0, color: const Color(0xFF000000))), + child: Center( + child: Text( + text, + style: TextStyle(fontSize: 20.0), + )))); + } +} diff --git a/src/flutter/lib/Components/ListItem.dart b/src/flutter/lib/Components/ListItem.dart new file mode 100644 index 0000000..32c77a0 --- /dev/null +++ b/src/flutter/lib/Components/ListItem.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class ListItem extends StatelessWidget { + ListItem({this.left, this.right}); + + final int left; + final String right; + + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.only(right: 4.0), + child: Text('${left.toString()}.', + style: TextStyle(fontWeight: FontWeight.bold))), + Text(right, style: TextStyle()) + ], + ); + } +} diff --git a/src/flutter/lib/Components/Logo.dart b/src/flutter/lib/Components/Logo.dart new file mode 100644 index 0000000..75be034 --- /dev/null +++ b/src/flutter/lib/Components/Logo.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class Logo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Stack(overflow: Overflow.visible, children: [ + Text( + "QR NOTIFICATOR", + style: TextStyle(fontFamily: "Jaapokki", fontSize: 30.0), + ), + Positioned( + right: -20.0, + top: -5.0, + child: Container( + width: 20.0, + height: 20.0, + child: Center( + child: Text('2', style: TextStyle(fontFamily: "Jaapokki"))), + decoration: new BoxDecoration( + color: Colors.orange, + shape: BoxShape.circle, + )), + ) + ]); + } +} diff --git a/src/flutter/lib/Components/SyncIcon.dart b/src/flutter/lib/Components/SyncIcon.dart new file mode 100644 index 0000000..5cd37bf --- /dev/null +++ b/src/flutter/lib/Components/SyncIcon.dart @@ -0,0 +1,37 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/animation.dart'; + +class SyncIcon extends StatefulWidget { + _SyncIcon createState() => _SyncIcon(); +} + +class _SyncIcon extends State with SingleTickerProviderStateMixin { + Animation animation; + AnimationController controller; + + initState() { + super.initState(); + controller = AnimationController( + duration: const Duration(milliseconds: 2000), vsync: this); + animation = Tween(begin: 0.0, end: pi * 2).animate(controller); + animation.addListener(() => setState(() {})); + controller.repeat(); + } + + Widget build(BuildContext context) { + return Transform.rotate( + angle: animation.value, + child: Container( + margin: EdgeInsets.symmetric(vertical: 10.0), + height: 32.0, + width: 32.0, + child: Image.asset('images/sync.png'), + )); + } + + dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/src/flutter/lib/Screens/Home.dart b/src/flutter/lib/Screens/Home.dart new file mode 100644 index 0000000..38bf326 --- /dev/null +++ b/src/flutter/lib/Screens/Home.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import '../Components/BG.dart'; +import '../Components/Button.dart'; +import '../Components/ListItem.dart'; +import '../Components/Logo.dart'; + +class Home extends StatelessWidget { + @override + Widget build(BuildContext context) { + return BG( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding(padding: EdgeInsets.only(bottom: 80.0), child: Logo()), + Button( + callback: () { + // Navigator.pushNamed(context, '/scan'); + Navigator.pushNamed(context, '/success/1'); + }, + text: "Scan", + ), + Padding( + padding: const EdgeInsets.only(top: 100.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListItem(left: 1, right: 'Open Camera.'), + ListItem(left: 2, right: 'Scan QR code.'), + ListItem(left: 2, right: 'Get notified.'), + ], + )) + ], + ))); + } +} diff --git a/src/flutter/lib/Screens/Scan.dart b/src/flutter/lib/Screens/Scan.dart new file mode 100644 index 0000000..3a7abcd --- /dev/null +++ b/src/flutter/lib/Screens/Scan.dart @@ -0,0 +1,39 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:barcode_scan/barcode_scan.dart'; + +import '../Components/BG.dart'; +import '../Components/SyncIcon.dart'; + +class Scan extends StatelessWidget { + Future scan() async { + try { + String barcode = await BarcodeScanner.scan(); + print('Success'); + print(barcode); + Navigator.pushNamed(null, '/home'); + } on PlatformException catch (e) { + if (e.code == BarcodeScanner.CameraAccessDenied) + print('The user did not grant the camera permission!'); + } catch (e) { + print('Nope'); + print(e); + } + } + + @override + Widget build(BuildContext context) => BG( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Scanning...', + style: TextStyle(fontWeight: FontWeight.bold), + ), + SyncIcon() + ], + ))); +} diff --git a/src/flutter/lib/Screens/Success.dart b/src/flutter/lib/Screens/Success.dart new file mode 100644 index 0000000..7196dc8 --- /dev/null +++ b/src/flutter/lib/Screens/Success.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +import '../Components/BG.dart'; +import '../Components/Button.dart'; + +class Success extends StatelessWidget { + final int result; + + Success({@required this.result}) { + final initializationSettings = InitializationSettings( + AndroidInitializationSettings('app_icon'), IOSInitializationSettings()); + final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin() + ..initialize(initializationSettings); + + flutterLocalNotificationsPlugin.show( + this.result, + '🚀 Scan succeded!', + 'Your super sercret code is: $result', + NotificationDetails( + AndroidNotificationDetails('your channel id', 'your channel name', + 'your channel description', + importance: Importance.Max, priority: Priority.High), + IOSNotificationDetails()), + ); + } + + @override + Widget build(BuildContext context) => BG( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.only(top: 150.0, bottom: 100.0), + child: Button( + text: 'Back', + callback: () => + Navigator.popUntil(context, ModalRoute.withName('/')), + ), + ), + Text( + 'Success', + style: TextStyle(fontWeight: FontWeight.bold), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10.0), + height: 32.0, + width: 32.0, + child: Image.asset('images/check.png'), + ) + ], + ))); +} diff --git a/src/flutter/lib/main.dart b/src/flutter/lib/main.dart new file mode 100644 index 0000000..137e5f7 --- /dev/null +++ b/src/flutter/lib/main.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +import 'Screens/Home.dart'; +import 'Screens/Scan.dart'; +import 'Screens/Success.dart'; + +void main() => runApp(App()); + +class App extends StatelessWidget { + @override + Widget build(BuildContext context) => MaterialApp( + routes: { + '/': (context) => Home(), + '/scan': (context) => Scan(), + }, + onGenerateRoute: (routeSettings) { + var path = routeSettings.name.split('/'); + final number = path.length > 1 ? int.parse(path[2]) : null; + + return MaterialPageRoute( + builder: (context) => Success(result: number), + settings: routeSettings, + ); + }); +}