From 231ed2c9df11e67bebdc7832c399f56cb2ae36ca Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 20 Aug 2019 13:54:45 +0100 Subject: [PATCH] many UI improvements and subtle tweaks --- lib/common/apifunctions/categories.dart | 2 +- .../apifunctions/find_organisations.dart | 2 +- lib/common/apifunctions/get_graph_data.dart | 16 +- .../apifunctions/request_login_api.dart | 35 ++-- .../apifunctions/request_logout_api.dart | 2 +- .../apifunctions/submit_receipt_api.dart | 2 +- .../widgets/animatedGradientButton.dart | 27 ++++ lib/env/dev.g.dart | 2 +- lib/env/dev.json | 2 +- lib/env/prod.g.dart | 2 +- lib/env/prod.json | 2 +- lib/pages/login_page.dart | 93 +++++------ lib/pages/receipt_page_2.dart | 149 ++++++++++-------- pubspec.lock | 7 + pubspec.yaml | 1 + 15 files changed, 191 insertions(+), 153 deletions(-) create mode 100644 lib/common/widgets/animatedGradientButton.dart diff --git a/lib/common/apifunctions/categories.dart b/lib/common/apifunctions/categories.dart index b00cc75..e9099b4 100644 --- a/lib/common/apifunctions/categories.dart +++ b/lib/common/apifunctions/categories.dart @@ -5,7 +5,7 @@ import 'package:local_spend/common/functions/get_token.dart'; import 'package:flutter/material.dart'; Future> getCategories() async { - const url = "https://dev.peartrade.org/api/search/category"; + const url = "https://dev.localspend.co.uk/api/search/category"; var token; await getToken().then((result) { diff --git a/lib/common/apifunctions/find_organisations.dart b/lib/common/apifunctions/find_organisations.dart index aa66ef9..b30479c 100644 --- a/lib/common/apifunctions/find_organisations.dart +++ b/lib/common/apifunctions/find_organisations.dart @@ -64,7 +64,7 @@ class Organisations { } Future> findOrganisations(String search) async { - final url = "https://dev.peartrade.org/api/search"; + final url = "https://dev.localspend.co.uk/api/search"; var token; await getToken().then((result) { diff --git a/lib/common/apifunctions/get_graph_data.dart b/lib/common/apifunctions/get_graph_data.dart index 8846917..862582d 100644 --- a/lib/common/apifunctions/get_graph_data.dart +++ b/lib/common/apifunctions/get_graph_data.dart @@ -28,7 +28,7 @@ class GraphData { ]; } - final url = "https://dev.peartrade.org/api/v1/customer/graphs"; + final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; SharedPreferences preferences = await SharedPreferences.getInstance(); Map body = { @@ -88,13 +88,13 @@ class GraphData { // TODO: Show available graph types based on whether or not the user is an Organisation or User - /// Customer graph types: https://dev.peartrade.org/api/v1/customer/graphs + /// Customer graph types: https://dev.localspend.co.uk/api/v1/customer/graphs /// - total_last_week /// - avg_spend_last_week /// - total_last_month /// - avg_spend_last_month /// - /// Organisations' graphs types: to fetch, POST to https://dev.peartrade.org/api/stats/[name] as {"session_key":"[boop beep]"} + /// Organisations' graphs types: to fetch, POST to https://dev.localspend.co.uk/api/stats/[name] as {"session_key":"[boop beep]"} /// - organisations_all : organisation /// - pies : organisation/pies /// - snippets : organisation/snippets @@ -110,7 +110,7 @@ class GraphData { /// HTTP POST request sample: /// {"graph":"total_last_week","session_key":"blahblahblah"} - final url = "https://dev.peartrade.org/api/v1/customer/graphs"; + final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; SharedPreferences preferences = await SharedPreferences.getInstance(); Map body = { @@ -182,7 +182,7 @@ class OrgGraphData { ]; } - final url = "https://dev.peartrade.org/api/v1/customer/graphs"; + final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; SharedPreferences preferences = await SharedPreferences.getInstance(); Map body = { @@ -242,13 +242,13 @@ class OrgGraphData { // TODO: Show available graph types based on whether or not the user is an Organisation or User - /// Customer graph types: https://dev.peartrade.org/api/v1/customer/graphs + /// Customer graph types: https://dev.localspend.co.uk/api/v1/customer/graphs /// - total_last_week /// - avg_spend_last_week /// - total_last_month /// - avg_spend_last_month /// - /// Organisations' graphs types: to fetch, POST to https://dev.peartrade.org/api/stats/[name] as {"session_key":"[boop beep]"} + /// Organisations' graphs types: to fetch, POST to https://dev.localspend.co.uk/api/stats/[name] as {"session_key":"[boop beep]"} /// - organisations_all : organisation /// - pies : organisation/pies /// - snippets : organisation/snippets @@ -264,7 +264,7 @@ class OrgGraphData { /// HTTP POST request sample: /// {"graph":"total_last_week","session_key":"blahblahblah"} - final url = "https://dev.peartrade.org/api/v1/customer/graphs"; + final url = "https://dev.localspend.co.uk/api/v1/customer/graphs"; SharedPreferences preferences = await SharedPreferences.getInstance(); Map body = { diff --git a/lib/common/apifunctions/request_login_api.dart b/lib/common/apifunctions/request_login_api.dart index 2f74052..10565d3 100644 --- a/lib/common/apifunctions/request_login_api.dart +++ b/lib/common/apifunctions/request_login_api.dart @@ -11,26 +11,27 @@ Future _incorrectDialog(BuildContext context, bool isLoginWrong) async { context: context, barrierDismissible: true, builder: (BuildContext context) { - return AlertDialog( - title: Text("Unable to Login"), - content: Text(isLoginWrong ? "Incorrect login details. Please try again." : "Our servers are having issues at the moment; sorry for the inconvenience. Please try again later."), - actions: [ - FlatButton( - child: Text('OK'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], + return AnimatedContainer( + duration: Duration(seconds: 2), + child : AlertDialog( + title: Text("Uh-oh!"), + content: Text(isLoginWrong ? "Incorrect login details. Please try again." : "Our servers are having issues at the moment; sorry for the inconvenience. Please try again later."), + actions: [ + FlatButton( + child: Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), ); }, ); } -Future requestLoginAPI( - BuildContext context, String email, String password) async { - //var apiUrl = ConfigWrapper.of(context).apiKey; - final url = "https://dev.peartrade.org/api/login"; +Future requestLoginAPI(BuildContext context, String email, String password) async { + final url = "https://dev.localspend.co.uk/api/login"; Map body = { 'email': email, @@ -43,7 +44,7 @@ Future requestLoginAPI( final response = await http.post( url, body: json.encode(body), - ); + ).timeout(Duration(seconds: 5)); if (response.statusCode == 200) { final responseJson = json.decode(response.body); @@ -61,6 +62,8 @@ Future requestLoginAPI( return null; } + } on TimeoutException catch (_) { + _incorrectDialog(context, false); } catch (error) { debugPrint(error.toString()); _incorrectDialog(context, false); diff --git a/lib/common/apifunctions/request_logout_api.dart b/lib/common/apifunctions/request_logout_api.dart index 342c85b..afa5b8d 100644 --- a/lib/common/apifunctions/request_logout_api.dart +++ b/lib/common/apifunctions/request_logout_api.dart @@ -8,7 +8,7 @@ import 'package:local_spend/common/functions/save_logout.dart'; import 'package:local_spend/model/json/login_model.dart'; Future requestLogoutAPI(BuildContext context) async { - final url = "https://dev.peartrade.org/api/logout"; + final url = "https://dev.localspend.co.uk/api/logout"; var token; diff --git a/lib/common/apifunctions/submit_receipt_api.dart b/lib/common/apifunctions/submit_receipt_api.dart index 9ea7462..7727c17 100644 --- a/lib/common/apifunctions/submit_receipt_api.dart +++ b/lib/common/apifunctions/submit_receipt_api.dart @@ -23,7 +23,7 @@ class Receipt { Future submitReceiptAPI( BuildContext context, Receipt receipt) async { //var apiUrl = ConfigWrapper.of(context).apiKey; - final url = "https://dev.peartrade.org/api/upload"; + final url = "https://dev.localspend.co.uk/api/upload"; SharedPreferences preferences = await SharedPreferences.getInstance(); diff --git a/lib/common/widgets/animatedGradientButton.dart b/lib/common/widgets/animatedGradientButton.dart new file mode 100644 index 0000000..c50d302 --- /dev/null +++ b/lib/common/widgets/animatedGradientButton.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:simple_animations/simple_animations.dart'; + +class AnimatedBackground extends StatelessWidget { + @override + Widget build(BuildContext context) { + final tween = MultiTrackTween([ + Track("color1").add(Duration(seconds: 4), + ColorTween(begin: Colors.lightBlue, end: Colors.lightBlue[300])), + ]); + + return ControlledAnimation( + playback: Playback.MIRROR, + tween: tween, + duration: tween.duration, + builder: (context, animation) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [animation["color1"], Colors.lightBlue])), + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/env/dev.g.dart b/lib/env/dev.g.dart index ad6c7c4..2ab2ce9 100644 --- a/lib/env/dev.g.dart +++ b/lib/env/dev.g.dart @@ -9,5 +9,5 @@ part of 'dev.dart'; const _$configJsonLiteral = { 'env': 'DEV', 'production': false, - 'apiUrl': 'https://dev.peartrade.org/api' + 'apiUrl': 'https://dev.localspend.co.uk/api' }; diff --git a/lib/env/dev.json b/lib/env/dev.json index 403630b..b2a9044 100644 --- a/lib/env/dev.json +++ b/lib/env/dev.json @@ -1,5 +1,5 @@ { "env": "DEV", "production": false, - "apiUrl": "https://dev.peartrade.org/api" + "apiUrl": "https://dev.localspend.co.uk/api" } \ No newline at end of file diff --git a/lib/env/prod.g.dart b/lib/env/prod.g.dart index 46e18c7..0d1854f 100644 --- a/lib/env/prod.g.dart +++ b/lib/env/prod.g.dart @@ -9,5 +9,5 @@ part of 'prod.dart'; const _$configJsonLiteral = { 'env': 'PROD', 'production': true, - 'apiUrl': 'https://www.peartrade.org/api' + 'apiUrl': 'https://www.localspend.co.uk/api' }; diff --git a/lib/env/prod.json b/lib/env/prod.json index ccdabad..fe2f989 100644 --- a/lib/env/prod.json +++ b/lib/env/prod.json @@ -1,5 +1,5 @@ { "env": "PROD", "production": true, - "apiUrl": "https://www.peartrade.org/api" + "apiUrl": "https://www.localspend.co.uk/api" } \ No newline at end of file diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index fc42e52..d3cec6a 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -18,6 +18,7 @@ class LoginPage extends StatefulWidget { } class LoginPageState extends State { + bool _isLoggingIn = false; final TextEditingController _emailController = TextEditingController(/*text: 'test@example.com'*/); // remove final TextEditingController _passwordController = TextEditingController(/*text: 'abc123'*/); // remove bool _saveLoginDetails = true; // I am extremely sorry for the placement of this variable @@ -69,6 +70,7 @@ class LoginPageState extends State { } login(String username, String password) async { + _isLoggingIn = true; SystemChannels.textInput.invokeMethod('TextInput.hide'); SharedPreferences preferences = await SharedPreferences.getInstance(); @@ -82,8 +84,9 @@ class LoginPageState extends State { print("details cleared"); } - requestLoginAPI(context, username, - password); + requestLoginAPI(context, username, password).then((value) { + _isLoggingIn = false; + }); } @override @@ -98,14 +101,6 @@ class LoginPageState extends State { } }, child: PlatformScaffold( -// drawer: BasicDrawer(), -// body: Container( -// decoration: BoxDecoration(color: Colors.white), -// margin: const EdgeInsets.all(20), -// child: Padding( -// padding: EdgeInsets.fromLTRB(30.0, 170.0, 30.0, 0.0), -// child: ListView( -// children: [ body: Container( decoration: new BoxDecoration( gradient: new LinearGradient( @@ -115,14 +110,15 @@ class LoginPageState extends State { end: Alignment.bottomCenter, ), ), - child: Container( + child: AnimatedContainer( + duration: Duration(seconds: 2), margin: EdgeInsets.fromLTRB(60,30,60,0), child: Column( children: [ Expanded( - child: Container( - margin: EdgeInsets.fromLTRB(15,0,15,0), -// alignment: FractionalOffset(0.5, 0.3), // not sure what this does ngl :/ + child: AnimatedContainer( + duration: Duration(seconds: 2), + margin: EdgeInsets.fromLTRB(15,0,15,0), decoration: BoxDecoration( image: DecorationImage( image: AssetImage('assets/images/launch_image.png') @@ -177,39 +173,42 @@ class LoginPageState extends State { Padding( padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0), - child : Material( - child: new Container( - decoration: new BoxDecoration( - border: new Border.all(color : Colors.transparent, width: 2), - borderRadius: BorderRadius.all(Radius.circular(2)), - gradient: new LinearGradient( - colors: [ - Colors.blue[300], - Colors.blue[500], - ], - stops: [0,1], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + child: Opacity( + opacity: _isLoggingIn ? 0.5 : 1, + child : Material( + child: new Container( + decoration: new BoxDecoration( + border: new Border.all(color : Colors.transparent, width: 2), + borderRadius: BorderRadius.all(Radius.circular(2)), + gradient: new LinearGradient( + colors: [ + Colors.blue[300], + Colors.blue[500], + ], + stops: [0,1], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), ), - ), - child : Material( - type: MaterialType.transparency, + child : Material( + type: MaterialType.transparency, - child : InkWell( - onTap: () => login( _emailController.text, _passwordController.text), - child: new Container( - width: 100, - height: 50, - child: new Center( - child: new Text( - 'GO', style: new TextStyle(fontSize: 18, color: Colors.white),), + child : InkWell( + onTap: _isLoggingIn ? null : () => login( _emailController.text, _passwordController.text), + child: new Container( + width: 100, + height: 50, + child: new Center( + child: new Text( + 'GO', style: new TextStyle(fontSize: 18, color: Colors.white),), + ), ), ), ), - ), + ), ), ), ), @@ -229,22 +228,6 @@ class LoginPageState extends State { }); }, ), - - /*child: LabeledCheckboxWithIcon( - label : "SAVE LOGIN", - textStyle: TextStyle(fontSize: 18, color: Colors.black54, fontWeight: FontWeight.bold), - icon: Icons.account_box, // need to remove icon padding!! - iconSize: 18, - iconColor: Colors.black54, - padding: const EdgeInsets.fromLTRB(0,0,0,0), - value : _saveLoginDetails, - - onChanged: (bool newValue) { - setState(() { - _saveLoginDetails = newValue; - }); - }, - ),*/ ), ], ), diff --git a/lib/pages/receipt_page_2.dart b/lib/pages/receipt_page_2.dart index 70d67f1..e526f43 100644 --- a/lib/pages/receipt_page_2.dart +++ b/lib/pages/receipt_page_2.dart @@ -3,6 +3,7 @@ import 'package:local_spend/common/platform/platform_scaffold.dart'; import 'package:intl/intl.dart'; import 'dart:core'; import 'package:flutter/cupertino.dart'; +import 'package:local_spend/common/widgets/animatedGradientButton.dart'; import 'package:local_spend/common/apifunctions/find_organisations.dart'; import 'package:local_spend/common/widgets/organisations_dialog.dart'; import 'package:local_spend/common/apifunctions/submit_receipt_api.dart'; @@ -72,7 +73,7 @@ class ReceiptPage2State extends State { // "purchase_time":"2019-08-12T11:06:00.000+01:00", // "organisation_id":59661, // "essential":false, - // "session_key":"C438432A-B775-11E9-8EE8-147589E69626" + // "session_key":"C438440A-B775-11E9-8EE8-147589E69626" // } Receipt receipt = new Receipt(); @@ -149,7 +150,7 @@ class ReceiptPage2State extends State { child : Text( "Receipt Details", style: TextStyle( - fontSize: 24, + fontSize: 26, color: Colors.grey[700], fontWeight: FontWeight.bold, ), @@ -166,17 +167,18 @@ class ReceiptPage2State extends State { child : Text( "Date/Time", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 110, + width: MediaQuery.of(context).size.width * 0.3, ), Container( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - height: 32.0, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: RaisedButton( onPressed: () { showModalBottomSheet( @@ -208,7 +210,7 @@ class ReceiptPage2State extends State { ? '${new DateFormat.MMMd().format(transaction.date)}' + ", " + '${new DateFormat.Hm().format(transaction.date)}' : '${new DateFormat.MMMd().format(transaction.date)}' + " " + transaction.date.year.toString() + ", " + '${new DateFormat.Hm().format(transaction.date)}', style: - TextStyle(color: Colors.white, fontSize: 18.0), + TextStyle(color: Colors.white, fontSize: 22.0), ), color: Colors.blue, ), @@ -228,17 +230,18 @@ class ReceiptPage2State extends State { child : Text( "Payee", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 110, + width: MediaQuery.of(context).size.width * 0.3, ), Container( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - height: 32.0, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: RaisedButton( onPressed: () { var organisations = new FindOrganisations(); @@ -261,7 +264,7 @@ class ReceiptPage2State extends State { ? transaction.organisation.name.substring(0,12) + "..." : transaction.organisation.name, style: - TextStyle(color: Colors.white, fontSize: 18.0), + TextStyle(color: Colors.white, fontSize: 22.0), ), color: Colors.blue, ), @@ -281,17 +284,18 @@ class ReceiptPage2State extends State { child : Text( "Recurring", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 110, + width: MediaQuery.of(context).size.width * 0.3, ), Container( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - height: 32.0, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: RaisedButton( onPressed: () { showModalBottomSheet( @@ -306,7 +310,7 @@ class ReceiptPage2State extends State { transaction.recurring = _sampleRecurringOptions[newValue]; setState(() {}); }), - itemExtent: 32, + itemExtent: 40, ), ); }); @@ -316,7 +320,7 @@ class ReceiptPage2State extends State { ? 'None' : transaction.recurring, style: - TextStyle(color: Colors.white, fontSize: 18.0), + TextStyle(color: Colors.white, fontSize: 22.0), ), color: Colors.blue, ), @@ -334,17 +338,18 @@ class ReceiptPage2State extends State { child : Text( "Category", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 110, + width: MediaQuery.of(context).size.width * 0.3, ), Container( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - height: 32.0, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: Tooltip( message: "Category of transaction", child: RaisedButton( @@ -361,7 +366,7 @@ class ReceiptPage2State extends State { transaction.category = _categories[newValue]; setState(() {}); }), - itemExtent: 32, + itemExtent: 40, ), ); }); @@ -371,7 +376,7 @@ class ReceiptPage2State extends State { ? 'None' : transaction.category, style: - TextStyle(color: Colors.white, fontSize: 18.0), + TextStyle(color: Colors.white, fontSize: 22.0), ), color: Colors.blue, ), @@ -391,16 +396,17 @@ class ReceiptPage2State extends State { child : Text( "Essential", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 95, + width: MediaQuery.of(context).size.width * 0.3, ), Container( - height: 32.0, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: Checkbox( value: transaction.isEssential, onChanged: ((value) { @@ -423,19 +429,19 @@ class ReceiptPage2State extends State { child : Text( "Amount", style: TextStyle( - fontSize: 18, + fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold, ), ), - width: 110, + width: MediaQuery.of(context).size.width * 0.3, ), Container( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - height: 32.0, - width: 100, + height: 40.0, + width: MediaQuery.of(context).size.width * 0.6, child: TextField( controller: transaction.amount, decoration: InputDecoration( @@ -454,46 +460,57 @@ class ReceiptPage2State extends State { child: Tooltip( message: "Submit receipt", child: Container( - height: 65.0, - child: RaisedButton( - onPressed: () { - try { - if (transaction.amount.text == "" || transaction.organisation.name == null) { - showDialog( - context: context, - builder: (BuildContext context) { - return _invalidDialog(context); + decoration: new BoxDecoration( + border: new Border.all(color : Colors.transparent, width: 2), + borderRadius: BorderRadius.all(Radius.circular(2)), + ), + height: 75.0, + child: Stack( + children: [ + AnimatedBackground(), + Material( + type: MaterialType.transparency, + child: InkWell( + child: Center( + child : Text("GO", + style: + TextStyle(color: Colors.white, fontSize: 30.0), + ), + ), + onTap: () { + try { + if (transaction.amount.text == "" || transaction.organisation.name == null) { + showDialog( + context: context, + builder: (BuildContext context) { + return _invalidDialog(context); + } + ); + } else { + if (double.tryParse(transaction.amount.text) != null && double.tryParse(transaction.amount.text) > 0) { + _submitReceipt(transaction); + } else { + showDialog( + context: context, + builder: (BuildContext context) { + return _invalidDialog(context); + } + ); + } } - ); - } else { - if (double.tryParse(transaction.amount.text) != null && double.tryParse(transaction.amount.text) > 0) { - _submitReceipt(transaction); - } else { - showDialog( - context: context, - builder: (BuildContext context) { - return _invalidDialog(context); - } - ); - } - } - } - catch (_) { - showDialog( - context: context, - builder: (BuildContext context) { - return _invalidDialog(context); - } - ); - } - }, - - - child: Text("GO", - style: - TextStyle(color: Colors.white, fontSize: 22.0), - ), - color: Colors.blue, + } + catch (_) { + showDialog( + context: context, + builder: (BuildContext context) { + return _invalidDialog(context); + } + ); + } + }, + ), + ), + ], ), ), ), diff --git a/pubspec.lock b/pubspec.lock index cc8d2b8..8db997c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -394,6 +394,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.3" + simple_animations: + dependency: "direct main" + description: + name: simple_animations + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.3" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index c05bf61..385c2e0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: flutter_linkify: ^1.0.3 flutter_fadein: ^1.1.1 charts_flutter: ^0.6.0 + simple_animations: ^1.3.3 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2