From 90e3fc1dcfa6913e48c873eb05f4a209a4b0dbe8 Mon Sep 17 00:00:00 2001 From: Jakub Timko <38620273+Timmynek@users.noreply.github.com> Date: Sun, 20 Nov 2022 11:51:01 +0100 Subject: [PATCH] Introducing login screen (#2) * Add gradle signing configuration * Add login screen frontend * Introduce constants * Extracting button to custom widget. * Change button color. * Extract text field to custom widget. * Fix overflowing problem on login screen. * Extract clickable text to custom widget. * Introducing checkbox to remember user on login screen * Changing text field style * Adding simple home screen, provider and routing * Adding simple login form validation * Introducing newUser method in user model and unsetting user credentials after logout * Commenting out wrapper for now * Adding form validation error message to constants * Fixing typo in a file name * Introducing wrapper which checks whether the user is already logged in. Co-authored-by: Jakub Timko --- assets/placeholder-image.png | Bin 0 -> 8806 bytes assets/zj-logo.svg | 72 ++++++ lib/constants.dart | 18 ++ lib/main.dart | 141 +++--------- lib/models/user.dart | 18 ++ lib/routes.dart | 32 +++ lib/ui/screens/home.dart | 33 +++ lib/ui/screens/login.dart | 118 ++++++++++ lib/ui/screens/wrapper.dart | 36 +++ lib/ui/widgets/button.dart | 27 +++ lib/ui/widgets/checkbox.dart | 29 +++ lib/ui/widgets/clickableText.dart | 28 +++ lib/ui/widgets/textField.dart | 47 ++++ pubspec.lock | 370 +++++++++++++++++------------- pubspec.yaml | 183 +++++++-------- 15 files changed, 786 insertions(+), 366 deletions(-) create mode 100644 assets/placeholder-image.png create mode 100644 assets/zj-logo.svg create mode 100644 lib/constants.dart create mode 100644 lib/models/user.dart create mode 100644 lib/routes.dart create mode 100644 lib/ui/screens/home.dart create mode 100644 lib/ui/screens/login.dart create mode 100644 lib/ui/screens/wrapper.dart create mode 100644 lib/ui/widgets/button.dart create mode 100644 lib/ui/widgets/checkbox.dart create mode 100644 lib/ui/widgets/clickableText.dart create mode 100644 lib/ui/widgets/textField.dart diff --git a/assets/placeholder-image.png b/assets/placeholder-image.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c902e98cd753bf25db5cecff4fd00edf57f89b GIT binary patch literal 8806 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/constants.dart b/lib/constants.dart new file mode 100644 index 00000000..ad02a8bd --- /dev/null +++ b/lib/constants.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class ZachranObedColors { + static const primary = Color.fromRGBO(226, 16, 19, 1); + static const primaryLight = Color.fromRGBO(250, 71, 71, 1); +} + +class ZachranObedStrings { + static const emailAddress = 'E-mailová adresa'; + static const password = 'Heslo'; + static const login = 'Přihlásit se'; + static const forgottenPassword = 'Zapomněl/a jsem heslo'; + static const rememberUser = 'Zapamatovat'; + static const requiredFieldError = 'Vyplňte prosím toto pole'; + + static const zjLogoPath = 'assets/zj-logo.svg'; + static const placeholderImagePath = 'assets/placeholder-image.png'; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index e0160294..e17ba2c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,115 +1,26 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:zachranobed/models/user.dart'; +import 'package:zachranobed/routes.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => User(), + builder: (context, child) { + return const MaterialApp( + initialRoute: RouteManager.wrapper, + onGenerateRoute: RouteManager.generateRoute, + ); + }, + ); + } +} diff --git a/lib/models/user.dart b/lib/models/user.dart new file mode 100644 index 00000000..d8c04d5f --- /dev/null +++ b/lib/models/user.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class User extends ChangeNotifier { + + String email = ""; + String password = ""; + + /*User(this.email, this.password) { + notifyListeners(); + }*/ + + void newUser(String email, String password) { + this.email = email; + this.password = password; + notifyListeners(); + } + +} \ No newline at end of file diff --git a/lib/routes.dart b/lib/routes.dart new file mode 100644 index 00000000..48e4f625 --- /dev/null +++ b/lib/routes.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:zachranobed/ui/screens/home.dart'; +import 'package:zachranobed/ui/screens/login.dart'; +import 'package:zachranobed/ui/screens/wrapper.dart'; + +class RouteManager { + static const String wrapper = '/'; + static const String login = '/login'; + static const String home = '/home'; + + static Route generateRoute(RouteSettings settings) { + switch (settings.name) { + case wrapper: + return MaterialPageRoute( + builder: (context) => const Wrapper(), + ); + + case login: + return MaterialPageRoute( + builder: (context) => const Login(), + ); + + case home: + return MaterialPageRoute( + builder: (context) => const Home(), + ); + + default: + throw const FormatException('Route not found!'); + } + } +} \ No newline at end of file diff --git a/lib/ui/screens/home.dart b/lib/ui/screens/home.dart new file mode 100644 index 00000000..efc5ae52 --- /dev/null +++ b/lib/ui/screens/home.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:zachranobed/models/user.dart'; +import 'package:zachranobed/routes.dart'; + +class Home extends StatelessWidget { + const Home({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Home'), + actions: [ + IconButton( + onPressed: () { + Provider.of(context, listen: false).newUser('', ''); + Navigator.of(context).pushReplacementNamed(RouteManager.login); + }, + icon: Icon(Icons.exit_to_app), + ), + ], + ), + body: Center( + child: Consumer( + builder: (context, user, child) { + return Text('Ahoj ${user.email}'); + }, + ), + ), + ); + } +} diff --git a/lib/ui/screens/login.dart b/lib/ui/screens/login.dart new file mode 100644 index 00000000..be995f79 --- /dev/null +++ b/lib/ui/screens/login.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; +import 'package:zachranobed/constants.dart'; +import 'package:zachranobed/models/user.dart'; +import 'package:zachranobed/routes.dart'; +import 'package:zachranobed/ui/widgets/button.dart'; +import 'package:zachranobed/ui/widgets/checkbox.dart'; +import 'package:zachranobed/ui/widgets/clickableText.dart'; +import 'package:zachranobed/ui/widgets/textField.dart'; + +class Login extends StatefulWidget { + const Login({Key? key}) : super(key: key); + + @override + State createState() => _LoginState(); +} + +class _LoginState extends State { + final _formKey = GlobalKey(); + + TextEditingController emailController = TextEditingController(); + TextEditingController passwordController = TextEditingController(); + + bool _rememberUser = false; + + @override + void dispose() { + emailController.dispose(); + passwordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: SingleChildScrollView( + child: Column( + children: [ + SvgPicture.asset( + ZachranObedStrings.zjLogoPath, + color: ZachranObedColors.primary, + ), + const SizedBox(height: 20), + + Container( + decoration: BoxDecoration( + border: Border.all(width: 2) + ), + child: Image.asset( + ZachranObedStrings.placeholderImagePath, + + ), + ), + const SizedBox(height: 20), + + Form( + key: _formKey, + child: Column( + children: [ + ZachranObedTextField( + text: ZachranObedStrings.emailAddress, + controller: emailController, + onValidation: (val) => val!.isEmpty ? ZachranObedStrings.requiredFieldError : null, + ), + const SizedBox(height: 15), + + ZachranObedTextField( + text: ZachranObedStrings.password, + controller: passwordController, + obscureText: true, + onValidation: (val) => val!.isEmpty ? ZachranObedStrings.requiredFieldError : null, + ), + const SizedBox(height: 10), + + ZachranObedCheckbox( + text: ZachranObedStrings.rememberUser, + isChecked: _rememberUser, + whatIsChecked: (bool value) => setState(() { + _rememberUser = value; + print(_rememberUser); + }) + ), + + ZachranObedButton( + text: ZachranObedStrings.login.toUpperCase(), + onPressed: () { + if (_formKey.currentState!.validate()) { + Provider.of(context, listen: false).newUser( + emailController.text, passwordController.text); + Navigator.of(context).pushReplacementNamed(RouteManager.home); + } + }, + ), + ], + ) + ), + const SizedBox(height: 15), + + ZachranObedClickableText( + text: ZachranObedStrings.forgottenPassword, + onTap: () { + print('Change password'); + } + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/wrapper.dart b/lib/ui/screens/wrapper.dart new file mode 100644 index 00000000..3efd1def --- /dev/null +++ b/lib/ui/screens/wrapper.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:zachranobed/models/user.dart'; +import 'package:zachranobed/routes.dart'; + +class Wrapper extends StatefulWidget { + const Wrapper({Key? key}) : super(key: key); + + @override + State createState() => _WrapperState(); +} + +class _WrapperState extends State { + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_){ + _loadUserInfo(); + }); + } + + _loadUserInfo() { + User user = context.read(); + if (user.email == "") { + Navigator.of(context).pushReplacementNamed(RouteManager.login); + } else { + Navigator.of(context).pushReplacementNamed(RouteManager.home); + } + } + + @override + Widget build(BuildContext context) { + return const Scaffold(body: Center(child: CircularProgressIndicator())); + } +} diff --git a/lib/ui/widgets/button.dart b/lib/ui/widgets/button.dart new file mode 100644 index 00000000..9c633741 --- /dev/null +++ b/lib/ui/widgets/button.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:zachranobed/constants.dart'; + +class ZachranObedButton extends StatelessWidget { + const ZachranObedButton({ + Key? key, + required this.text, + required this.onPressed, + }) : super(key: key); + + final String text; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: Size.fromHeight(50), + backgroundColor: ZachranObedColors.primaryLight, + ), + onPressed: onPressed, + child: Text( + text, + ), + ); + } +} diff --git a/lib/ui/widgets/checkbox.dart b/lib/ui/widgets/checkbox.dart new file mode 100644 index 00000000..3c1923ac --- /dev/null +++ b/lib/ui/widgets/checkbox.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:zachranobed/constants.dart'; + +class ZachranObedCheckbox extends StatelessWidget { + const ZachranObedCheckbox({ + Key? key, + required this.text, + this.isChecked = false, + required this.whatIsChecked, + }) : super(key: key); + + final String text; + final bool isChecked; + final ValueChanged whatIsChecked; + + @override + Widget build(BuildContext context) { + return CheckboxListTile( + contentPadding: EdgeInsets.zero, + title: Text(text), + activeColor: ZachranObedColors.primaryLight, + value: isChecked, + onChanged: (bool? value) { + whatIsChecked(value!); + }, + controlAffinity: ListTileControlAffinity.leading, + ); + } +} diff --git a/lib/ui/widgets/clickableText.dart b/lib/ui/widgets/clickableText.dart new file mode 100644 index 00000000..a8011ed9 --- /dev/null +++ b/lib/ui/widgets/clickableText.dart @@ -0,0 +1,28 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class ZachranObedClickableText extends StatelessWidget { + const ZachranObedClickableText({ + Key? key, + required this.text, + required this.onTap + }) : super(key: key); + + final String text; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return RichText( + text: TextSpan( + text: text, + style: TextStyle( + color: Colors.black, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = onTap + ), + ); + } +} diff --git a/lib/ui/widgets/textField.dart b/lib/ui/widgets/textField.dart new file mode 100644 index 00000000..4dec4b00 --- /dev/null +++ b/lib/ui/widgets/textField.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class ZachranObedTextField extends StatefulWidget { + const ZachranObedTextField({ + Key? key, + required this.text, + this.obscureText = false, + required this.controller, + this.onValidation, + }) : super(key: key); + + final String text; + final bool obscureText; + final TextEditingController controller; + final String? Function(String?)? onValidation; + + @override + State createState() => _ZachranObedTextFieldState(); +} + +class _ZachranObedTextFieldState extends State { + + final TextFieldBorder = const OutlineInputBorder( + borderSide: BorderSide( + width: 2, + color: Colors.black, + ), + borderRadius: BorderRadius.zero, + ); + + @override + Widget build(BuildContext context) { + return TextFormField( + controller: widget.controller, + obscureText: widget.obscureText, + cursorColor: Colors.black, + validator: widget.onValidation, + decoration: InputDecoration( + labelText: widget.text, + labelStyle: TextStyle(color: Colors.grey[600]), + enabledBorder: TextFieldBorder, + focusedBorder: TextFieldBorder, + focusColor: Colors.redAccent, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index e9b9197d..f7e103e6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,160 +1,210 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.9.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.16.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.5" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.12" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.5" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.9.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.12" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" -sdks: - dart: ">=2.18.2 <3.0.0" +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" +sdks: + dart: ">=2.18.2 <3.0.0" + flutter: ">=2.11.0-0.1.pre" diff --git a/pubspec.yaml b/pubspec.yaml index 7d0a1f46..a26d22c7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,91 +1,92 @@ -name: zachranobed -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 - -environment: - sdk: '>=2.18.2 <3.0.0' - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages +name: zachranobed +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.18.2 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + flutter_svg: ^1.1.6 + provider: ^6.0.4 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/ + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages