diff --git a/assets/dash.png b/assets/dash.png new file mode 100644 index 0000000..6e952fe Binary files /dev/null and b/assets/dash.png differ diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index d8da97c..46bf275 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -22,141 +22,167 @@ class _HomeScreenState extends State { final chatHandler = ChatHandler(); final authHandler = AuthHandeler(); late user.User? currentUser; + bool _isLoading = true; @override void initState() { super.initState(); + authHandler.getData(email: _auth.currentUser!.email!).then((value) { setState(() { currentUser = value; + _isLoading = false; }); }); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - title: Text('Welcome ${currentUser!.username} !'), - actions: [ - TextButton( - onPressed: () { - _auth.signOut(); - Navigator.pushNamed(context, 'Login'); - }, - child: const Text( - 'Logout', - style: TextStyle(color: Colors.white), - )), - ], - ), - body: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: StreamBuilder( - stream: FirebaseFirestore.instance - .collection('chats') - .orderBy('createdAt', descending: false) - .snapshots(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator(), - ); - } else if (snapshot.hasError) { - return Text("Something Happned Wrong!"); - } else if (snapshot.hasData) { - final List chats = List.from(snapshot.data!.docs - .map((e) => Chat.fromMap(e.data())) - .toList()); - return Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: ListView.builder( - shrinkWrap: true, - itemCount: chats.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text(chats[index].username!), - SizedBox( - height: 10, - ), - BubbleSpecialThree( - text: chats[index].message!, - color: Color(0xFF1B97F3), - tail: true, - textStyle: TextStyle( - color: Colors.white, fontSize: 16), - ), - ], + return _isLoading + ? Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + SizedBox( + height: 50, + ), + CircularProgressIndicator(), + SizedBox( + height: 50, + ), + Text('Fetching Current Messsages'), + ], + ), + ), + ) + : Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text('Welcome ${currentUser!.username} !'), + actions: [ + TextButton( + onPressed: () { + _auth.signOut(); + Navigator.pushNamed(context, 'Login'); + }, + child: const Text( + 'Logout', + style: TextStyle(color: Colors.white), + )), + ], + ), + body: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('chats') + .orderBy('createdAt', descending: false) + .snapshots(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(), + ); + } else if (snapshot.hasError) { + return Text("Something Happned Wrong!"); + } else if (snapshot.hasData) { + final List chats = List.from(snapshot.data!.docs + .map((e) => Chat.fromMap(e.data())) + .toList()); + return Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: chats.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Text(chats[index].username!), + SizedBox( + height: 10, + ), + BubbleSpecialThree( + text: chats[index].message!, + color: Color(0xFF1B97F3), + tail: true, + textStyle: TextStyle( + color: Colors.white, + fontSize: 16), + ), + ], + ), + ); + }, ), - ); - }, + ), + SizedBox( + height: 10, + ), + ], + ); + } else { + return Text("Something Happned Wrong!"); + } + }, + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 320, + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _msgController, + decoration: InputDecoration( + hintText: 'Type Your Msg', + border: InputBorder.none), + ), ), ), - SizedBox( - height: 10, - ), + IconButton( + onPressed: () { + if (_msgController.text == '') { + return; + } else { + chatHandler.sendMessage( + chat: Chat( + message: _msgController.text, + username: currentUser!.username, + createdAt: DateTime.now().toLocal().toString(), + )); + _msgController.clear(); + } + }, + icon: Icon(Icons.send, color: Colors.blueAccent), + iconSize: 50, + ) ], - ); - } else { - return Text("Something Happned Wrong!"); - } - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: 300, - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _msgController, - decoration: InputDecoration( - hintText: 'Type Your Msg', border: InputBorder.none), - ), ), ), - IconButton( - onPressed: () { - if (_msgController.text == '') { - return; - } else { - chatHandler.sendMessage( - chat: Chat( - message: _msgController.text, - username: currentUser!.username, - createdAt: DateTime.now().toLocal().toString(), - )); - _msgController.clear(); - } - }, - icon: Icon(Icons.send), - iconSize: 50, - ) ], ), - ), - ], - ), - ); + ); } } diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index e507947..97a5807 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -1,14 +1,24 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -class LoginScreen extends StatelessWidget { +import '../widgets/toast.dart'; + +class LoginScreen extends StatefulWidget { LoginScreen({super.key}); + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); final FirebaseAuth _auth = FirebaseAuth.instance; + bool _isLoading = false; + @override Widget build(BuildContext context) { return Scaffold( @@ -16,72 +26,136 @@ class LoginScreen extends StatelessWidget { automaticallyImplyLeading: false, title: const Text('Login user'), ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _emailController, - decoration: InputDecoration( - hintText: 'Email', border: InputBorder.none), - ), - ), - ), - const SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _passwordController, - decoration: InputDecoration( - hintText: 'Password', border: InputBorder.none), - ), - ), - ), - const SizedBox( - height: 10, - ), - ElevatedButton( - onPressed: () { - _auth.signInWithEmailAndPassword( - email: _emailController.text.toString(), - password: _passwordController.text.toString()); - Navigator.pushNamed(context, 'Home'); - }, - child: const Text('Login')), - const SizedBox( - height: 10, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Stack( children: [ - const Text("Don't have an account ?"), - TextButton( - onPressed: () { - Navigator.pushNamed(context, 'Register'); - }, - child: const Text( - 'Register', - style: - TextStyle(color: Colors.deepPurpleAccent, fontSize: 16), + AbsorbPointer( + absorbing: _isLoading, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 50, + ), + Image.asset( + 'assets/dash.png', + ), + const SizedBox( + height: 50, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _emailController, + decoration: InputDecoration( + hintText: 'Email', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _passwordController, + decoration: InputDecoration( + hintText: 'Password', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + ElevatedButton( + onPressed: () { + String errorMessage = 'Something Went Wrong!'; + setState(() { + _isLoading = true; + }); + + _auth + .signInWithEmailAndPassword( + email: _emailController.text.toString(), + password: + _passwordController.text.toString()) + .then((value) { + setState(() { + _isLoading = false; + }); + Navigator.pushNamedAndRemoveUntil( + context, 'Home', (route) => false); + }).catchError((error) { + setState(() { + _isLoading = false; + }); + switch (error.code) { + case "invalid-email": + errorMessage = "Invalid Email Provided"; + break; + case "user-not-found": + errorMessage = + "There is no user corresponding to the given email"; + break; + case "wrong-password": + errorMessage = + "Password is invalid for the given email"; + break; + default: + errorMessage = "An undefined Error happened."; + } + debugPrint(error.code); + showToast(errorMessage); + }); + }, + child: const Text('Login')), + const SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Don't have an account ?"), + TextButton( + onPressed: () { + Navigator.pushNamed(context, 'Register'); + }, + child: const Text( + 'Register', + style: TextStyle( + color: Colors.deepPurpleAccent, fontSize: 16), + ), + ) + ], + ) + ], ), - ) + ), + if (_isLoading) + const Positioned( + top: 0, + left: 0, + right: 0, + bottom: 0, + child: Center( + child: CircularProgressIndicator(), + ), + ) ], - ) - ], + ), + ), ), ), ); diff --git a/lib/screens/register_screen.dart b/lib/screens/register_screen.dart index ebc6fc0..81c40d1 100644 --- a/lib/screens/register_screen.dart +++ b/lib/screens/register_screen.dart @@ -1,3 +1,4 @@ +import 'package:chatapp/widgets/toast.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; @@ -21,6 +22,7 @@ class _RegisterScreenState extends State { final FirebaseAuth _auth = FirebaseAuth.instance; final authHandler = AuthHandeler(); + bool _isLoading = false; @override Widget build(BuildContext context) { @@ -32,109 +34,158 @@ class _RegisterScreenState extends State { body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + child: Stack( children: [ - const SizedBox( - height: 50, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _nameController, - decoration: InputDecoration( - hintText: 'Name', border: InputBorder.none), - ), - ), - ), - const SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _usernameController, - decoration: InputDecoration( - hintText: 'Username', border: InputBorder.none), - ), - ), - ), - const SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _emailController, - decoration: InputDecoration( - hintText: 'email', border: InputBorder.none), - ), + AbsorbPointer( + absorbing: _isLoading, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 50, + ), + Image.asset( + 'assets/dash.png', + ), + const SizedBox( + height: 50, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _nameController, + decoration: InputDecoration( + hintText: 'Name', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _usernameController, + decoration: InputDecoration( + hintText: 'Username', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _emailController, + decoration: InputDecoration( + hintText: 'email', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: _passwordController, + decoration: InputDecoration( + hintText: 'Password', border: InputBorder.none), + ), + ), + ), + const SizedBox( + height: 10, + ), + ElevatedButton( + onPressed: () { + setState(() { + _isLoading = true; + }); + _auth + .createUserWithEmailAndPassword( + email: _emailController.text.toString(), + password: _passwordController.text.toString()) + .then((value) { + authHandler.registerUser( + user: user.User( + email: _emailController.text.toString(), + name: _nameController.text.toString(), + createdAt: DateTime.now().toLocal().toString(), + updatedAt: DateTime.now().toLocal().toString(), + username: _usernameController.text.toString(), + )); + showToast( + "User registered successfully, Login now!"); + Navigator.pushNamed(context, 'Login'); + }).catchError((error) { + String errorMessage = 'Something Went Wrong!'; + switch (error.code) { + case "email-already-in-use": + errorMessage = + "Email is already in use on different account"; + break; + default: + errorMessage = "An undefined Error happened."; + } + debugPrint(error.code); + showToast(errorMessage); + setState(() { + _isLoading = false; + }); + }); + }, + child: const Text('Register')), + const SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Already have an account ?"), + TextButton( + onPressed: () { + Navigator.pushNamed(context, 'Login'); + }, + child: const Text( + 'Login', + style: TextStyle( + color: Colors.deepPurpleAccent, fontSize: 16), + ), + ) + ], + ) + ], ), ), - const SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 1), - borderRadius: BorderRadius.circular(16)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - controller: _passwordController, - decoration: InputDecoration( - hintText: 'Password', border: InputBorder.none), + if (_isLoading) + const Positioned( + top: 0, + left: 0, + right: 0, + bottom: 0, + child: Center( + child: CircularProgressIndicator(), ), - ), - ), - ElevatedButton( - onPressed: () { - _auth - .createUserWithEmailAndPassword( - email: _emailController.text.toString(), - password: _passwordController.text.toString()) - .then((value) { - authHandler.registerUser( - user: user.User( - email: _emailController.text.toString(), - name: _nameController.text.toString(), - createdAt: DateTime.now().toLocal().toString(), - updatedAt: DateTime.now().toLocal().toString(), - username: _usernameController.text.toString(), - )); - }); - Navigator.pushNamed(context, 'Login'); - }, - child: const Text('Register')), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Text("Already have an account ?"), - InkWell( - onTap: () { - Navigator.pushNamed(context, 'Login'); - }, - child: const Text( - 'Login', - style: TextStyle( - color: Colors.deepPurpleAccent, fontSize: 21), - ), - ) - ], - ) + ) ], ), ), diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index 3478f0b..9a9993a 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -29,10 +29,36 @@ class _SplashScreenState extends State { @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Center( - child: FlutterLogo( - size: 80, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/dash.png', + ), + const SizedBox( + height: 50, + ), + const CircularProgressIndicator(), + const SizedBox( + height: 50, + ), + const Text('Welcome to Dash Chat', + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold)), + const SizedBox( + height: 50, + ), + const Text("Developed with ❤️ by @APP LEADS", + style: TextStyle( + color: Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w400)), + ], ), ), ); diff --git a/lib/widgets/toast.dart b/lib/widgets/toast.dart new file mode 100644 index 0000000..7e57ca6 --- /dev/null +++ b/lib/widgets/toast.dart @@ -0,0 +1,16 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +// flutter toast +void showToast(String msg) { + Fluttertoast.showToast( + msg: msg, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.SNACKBAR, + timeInSecForIosWeb: 1, + backgroundColor: Colors.black, + textColor: Colors.white, + fontSize: 15.0, + ); +} diff --git a/pubspec.lock b/pubspec.lock index d5a483f..5645a0f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -168,6 +168,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" + url: "https://pub.dev" + source: hosted + version: "8.2.1" http_parser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6dfd275..53f364c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,32 +1,11 @@ name: chatapp 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. +publish_to: 'none' version: 1.0.0+1 environment: sdk: '>=2.19.1 <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 @@ -34,62 +13,25 @@ dependencies: firebase_auth: ^4.3.0 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 cloud_firestore: ^4.4.5 intl: ^0.17.0 chat_bubbles: ^1.4.0 + fluttertoast: ^8.0.8 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 + assets: + - assets/ # - 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