diff --git a/assets/images/header.png b/assets/images/header.png new file mode 100644 index 0000000..a8ed4ae Binary files /dev/null and b/assets/images/header.png differ diff --git a/assets/svg/header.svg b/assets/svg/header.svg new file mode 100644 index 0000000..2a6a64d --- /dev/null +++ b/assets/svg/header.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/application/application.dart b/lib/application/application.dart index 3bbdad3..df57b45 100644 --- a/lib/application/application.dart +++ b/lib/application/application.dart @@ -1 +1,2 @@ +export 'bottom_navbar_cubit/bottom_navbar_cubit.dart'; export 'user_bloc/user_bloc.dart'; diff --git a/lib/application/bottom_navbar_cubit/bottom_navbar_cubit.dart b/lib/application/bottom_navbar_cubit/bottom_navbar_cubit.dart new file mode 100644 index 0000000..55e20ca --- /dev/null +++ b/lib/application/bottom_navbar_cubit/bottom_navbar_cubit.dart @@ -0,0 +1,12 @@ +// navigation_cubit.dart + +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../core/enums/enums.dart'; + + +class NavigationCubit extends Cubit { + NavigationCubit() : super(NavigationTab.homeTab); + + void updateTab(NavigationTab tab) => emit(tab); +} diff --git a/lib/configs/configs.dart b/lib/configs/configs.dart index 9ce9071..3ecd21a 100644 --- a/lib/configs/configs.dart +++ b/lib/configs/configs.dart @@ -1,8 +1,7 @@ +export 'app.dart'; export 'app_dimensions.dart'; export 'app_typography.dart'; export 'app_typography_ext.dart'; export 'space.dart'; export 'space_ext.dart'; export 'ui.dart'; - - diff --git a/lib/core/app/app.dart b/lib/core/app/app.dart index 6177edf..1a8e003 100644 --- a/lib/core/app/app.dart +++ b/lib/core/app/app.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../application/application.dart'; @@ -13,15 +14,17 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MultiBlocProvider( providers: [ + BlocProvider(create: (context) => di.sl()), BlocProvider( - create: (context) => di.sl()..add(CheckUser()), + create: (context) => di.sl(), //..add(CheckUser()), ), ], - child: const MaterialApp( + child: MaterialApp( debugShowCheckedModeBanner: false, title: appTitle, onGenerateRoute: AppRouter.onGenerateRoute, - initialRoute: AppRouter.splash, + onGenerateInitialRoutes: (initialRoute) => AppRouter.generateInitialRoutes(initialRoute), + initialRoute: AppRouter.root, ), ); } diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart index cddb313..99e0955 100644 --- a/lib/core/constants/assets.dart +++ b/lib/core/constants/assets.dart @@ -1,11 +1,11 @@ sealed class AppAssets { - //svg - + ///svg static const String Logo = 'assets/svg/logo/logo.svg'; static const String BoxesSvg = 'assets/svg/boxes.svg'; static const String Trucks = 'assets/svg/trucks.svg'; - //png + ///png static const String BoxesPng = 'assets/images/boxes.png'; static const String TrucksPng = 'assets/images/trucks.png'; + static const String Header = 'assets/images/header.png'; } diff --git a/lib/core/constants/colors.dart b/lib/core/constants/colors.dart index aaa6dd2..0b118af 100644 --- a/lib/core/constants/colors.dart +++ b/lib/core/constants/colors.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; sealed class AppColors { static const Color primary = Color(0xFF343FDE); static const Color yellow = Color(0xFFFFC71E); + static const Color surface = Color(0xFFEDEEFC); static const Color CommonCyan = Color(0xff68C4C6); static const Color GreyText = Color(0xff575757); static const Color LightGrey = Color(0xfff1f1f1); diff --git a/lib/core/core.dart b/lib/core/core.dart index 9c28970..c669079 100644 --- a/lib/core/core.dart +++ b/lib/core/core.dart @@ -6,3 +6,4 @@ export 'observer/observer.dart'; export 'usecases/usecases.dart'; export 'networkchecker/network_info.dart'; export 'utils/utils.dart'; +export 'enums/enums.dart'; diff --git a/lib/core/enums/enums.dart b/lib/core/enums/enums.dart new file mode 100644 index 0000000..546b205 --- /dev/null +++ b/lib/core/enums/enums.dart @@ -0,0 +1 @@ +enum NavigationTab { homeTab, categoriesTab, productsTap, cartTab, profileTab } diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart index 7368ba0..c0fd2be 100644 --- a/lib/core/router/app_router.dart +++ b/lib/core/router/app_router.dart @@ -4,10 +4,10 @@ import '../../presentation/presentation.dart'; import '../errors/exceptions.dart'; sealed class AppRouter { - static const String splash = '/'; + static const String splash = '/splash'; static const String splash2 = '/splash2'; static const String login = '/login'; - // static const String root = '/root'; + static const String root = '/root'; // static const String productDetails = '/product-details'; // static const String search = '/search'; // static const String filter = '/filter'; @@ -34,8 +34,8 @@ sealed class AppRouter { return MaterialPageRoute(builder: (_) => const Splash2Screen()); case login: return MaterialPageRoute(builder: (_) => const LoginScreen()); - // case root: - // return MaterialPageRoute(builder: (_) => const RootScreen()); + case root: + return MaterialPageRoute(builder: (_) => const RootScreen()); // case search: // return MaterialPageRoute(builder: (_) => const SearchScreen()); // case filter: @@ -84,4 +84,16 @@ sealed class AppRouter { throw const RouteException('Route not found!'); } } + + static List> generateInitialRoutes(String initialRoute) { + debugPrint('generateInitialRoutes $initialRoute'); + + return [ + MaterialPageRoute(builder: (context) => const RootScreen()), + if (initialRoute != AppRouter.root) + MaterialPageRoute( + builder: (context) => const SplashScreen(), + ), + ]; + } } diff --git a/lib/data/data_sources/remote/user_remote_data_source.dart b/lib/data/data_sources/remote/user_remote_data_source.dart index 5128b50..a3245b4 100644 --- a/lib/data/data_sources/remote/user_remote_data_source.dart +++ b/lib/data/data_sources/remote/user_remote_data_source.dart @@ -19,7 +19,21 @@ class UserRemoteDataSourceImpl implements UserRemoteDataSource { @override Future signIn(SignInParams params) async { debugPrint('signIn'); - final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-in'), + + const data = ''' + { + "token": "exampleToken123", + "user": { + "_id": "user123", + "firstName": "John", + "lastName": "Doe", + "email": "john.doe@example.com" + } + } + '''; + + return authenticationResponseModelFromJson(data); + /* final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-in'), headers: { 'Content-Type': 'application/json', }, @@ -33,7 +47,7 @@ class UserRemoteDataSourceImpl implements UserRemoteDataSource { throw CredentialFailure(); } else { throw ServerException(); - } + } */ } @override diff --git a/lib/di/cubits.dart b/lib/di/cubits.dart new file mode 100644 index 0000000..33eb415 --- /dev/null +++ b/lib/di/cubits.dart @@ -0,0 +1,11 @@ +import '../application/application.dart'; +import 'di.dart'; + +void registerCubits() { + // Navigation + sl.registerFactory(() => NavigationCubit()); + + //Notiications + // sl.registerLazySingleton(() => FlutterLocalNotificationsPlugin()); + // sl.registerFactory(() => NotificationsCubit(sl())); +} diff --git a/lib/di/di.dart b/lib/di/di.dart index 91aaf2a..b78aec4 100644 --- a/lib/di/di.dart +++ b/lib/di/di.dart @@ -1,6 +1,7 @@ import 'package:get_it/get_it.dart'; import 'common.dart'; +import 'cubits.dart'; import 'user.dart'; final sl = GetIt.instance; @@ -16,7 +17,7 @@ Future init() async { // registerOrderFeature(); // Register Cubits - // registerCubits(); + registerCubits(); // Register common dependencies registerCommonDependencies(); diff --git a/lib/main.dart b/lib/main.dart index 4502274..3092f27 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,5 +20,10 @@ Future main() async { DeviceOrientation.portraitDown, ]); + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarBrightness: Brightness.dark, + )); + runApp(Phoenix(child: const MyApp())); } diff --git a/lib/presentation/screens/login.dart b/lib/presentation/screens/login.dart index c1e901a..4c6b12f 100644 --- a/lib/presentation/screens/login.dart +++ b/lib/presentation/screens/login.dart @@ -3,11 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import '../../application/user_bloc/user_bloc.dart'; -import '../../configs/app.dart'; import '../../configs/configs.dart'; import '../../core/core.dart'; import '../../domain/domain.dart'; -import '../widgets/button.dart'; +import '../widgets/widgets.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -21,6 +20,13 @@ class _LoginScreenState extends State { final TextEditingController _passwordController = TextEditingController(); final _formKey = GlobalKey(); + void _nextScreen() { + Navigator.of(context).pushNamedAndRemoveUntil( + AppRouter.root, + (route) => false, + ); + } + @override void dispose() { _userNameController.dispose(); @@ -82,7 +88,7 @@ class _LoginScreenState extends State { /// form part SizedBox( - height: AppDimensions.normalize(145), + height: AppDimensions.normalize(165), child: Stack( clipBehavior: Clip.none, children: [ @@ -158,25 +164,40 @@ class _LoginScreenState extends State { /// gap Space.yf(), - SizedBox( - width: double.infinity, - child: AppButton( - textColor: AppColors.primary, - btnColor: AppColors.yellow, - onPressed: () { - if (_formKey.currentState!.validate()) { - context.read().add( - SignInUser( - SignInParams( - username: _userNameController.text, - password: _passwordController.text, - ), - ), - ); + BlocConsumer( + listener: (context, state) { + if (state is UserLogged) { + _nextScreen(); + } else if (state is UserLoggedFail) { + if (state.failure is CredentialFailure) { + showCredentialErrorDialog(context); + } else { + showAuthErrorDialog(context); } - }, - text: 'Yzarlap başlaň', - ), + } + }, + builder: (context, state) { + return SizedBox( + width: double.infinity, + child: AppButton( + textColor: Colors.white, + btnColor: AppColors.primary, + onPressed: () { + if (_formKey.currentState!.validate()) { + context.read().add( + SignInUser( + SignInParams( + username: _userNameController.text, + password: _passwordController.text, + ), + ), + ); + } + }, + text: 'Yzarlap başlaň', + ), + ); + }, ) ], ), diff --git a/lib/presentation/screens/orders.dart b/lib/presentation/screens/orders.dart new file mode 100644 index 0000000..b72ed29 --- /dev/null +++ b/lib/presentation/screens/orders.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; + +import '../../configs/configs.dart'; +import '../../core/core.dart'; +import '../widgets/order_header.dart'; + +class OrdersScreen extends StatelessWidget { + const OrdersScreen({super.key}); + + @override + Widget build(BuildContext context) { + App.init(context); + return Scaffold( + backgroundColor: AppColors.surface, + body: CustomScrollView( + slivers: [ + const SliverToBoxAdapter( + child: OrderHeader(), + ), + SliverToBoxAdapter( + child: Padding( + padding: Space.all(1, 1), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Sargytlarym', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'öz ýüküňizi yzarlaň', + style: TextStyle( + color: Colors.grey, + ), + ), + ], + ), + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildOrderCard(); + }, + childCount: 4, // items.length, + ), + ), + ], + ), + ); + } + + Widget _buildOrderCard() { + return Card( + color: Colors.white, + margin: Space.all(.8, 0.5), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '№ABC456789', + style: AppText.b1b, + ), + Text( + 'Genişleýin >', + style: AppText.b1b?.copyWith( + color: AppColors.primary, + ), + ), + ], + ), + const SizedBox(height: 8), + const Row( + children: [ + Icon(Icons.circle, color: Colors.green, size: 12), + SizedBox(width: 4), + Text('Ýolda'), + Spacer(), + Text('Ugradylan senesi: 16.07.2024'), + ], + ), + const SizedBox(height: 16), + const Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Nireden:'), + Text('Urumçy', style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 8), + Text('Nirede:'), + Text('Aşgabat', style: TextStyle(fontWeight: FontWeight.bold)), + ], + ), + Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('Ýer sany:'), + Text('10', style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 8), + Text('Göwrümi:'), + Text('1472,31', style: TextStyle(fontWeight: FontWeight.bold)), + ], + ), + SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Maşyn №:'), + Text('AA1234AA', style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 8), + Text('Dukan №:'), + Text('A1043', style: TextStyle(fontWeight: FontWeight.bold)), + ], + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/screens/root.dart b/lib/presentation/screens/root.dart new file mode 100644 index 0000000..bc4b789 --- /dev/null +++ b/lib/presentation/screens/root.dart @@ -0,0 +1,83 @@ +import 'package:cargo/core/core.dart'; +import 'package:cargo/presentation/screens/orders.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../application/application.dart'; +import '../../configs/configs.dart'; +import '../widgets/bottom_navbar.dart'; + +class RootScreen extends StatelessWidget { + const RootScreen({super.key}); + + Future onPopInvoked(BuildContext context) async { + return (await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text( + 'Exit Application', + style: TextStyle(color: AppColors.primary), + ), + content: const Text( + 'Are You Sure?', + ), + actions: [ + TextButton( + child: const Text( + 'Yes', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + SystemNavigator.pop(); + }, + ), + TextButton( + child: const Text( + 'No', + style: TextStyle(color: AppColors.primary), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + )) ?? + false; + } + + @override + Widget build(BuildContext context) { + App.init(context); + + return PopScope( + onPopInvoked: (didPop) => onPopInvoked(context), + child: Scaffold( + backgroundColor: AppColors.surface, + bottomNavigationBar: const BottomNavigation(), + body: Container( + color: AppColors.surface, + child: Center( + child: BlocBuilder( + builder: (context, activeTab) { + switch (activeTab) { + case NavigationTab.homeTab: + return const OrdersScreen(); + case NavigationTab.categoriesTab: + return const Text(' CategoriesScreen()'); + case NavigationTab.productsTap: + return const Text('ProductsListScreen()'); + default: + return const Text('HomeScreen()'); + } + }, + ), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screens/screens.dart b/lib/presentation/screens/screens.dart index e3aaf81..3ed80f2 100644 --- a/lib/presentation/screens/screens.dart +++ b/lib/presentation/screens/screens.dart @@ -1,3 +1,4 @@ export 'splash.dart'; export 'splash2.dart'; export 'login.dart'; +export 'root.dart'; diff --git a/lib/presentation/widgets/auth_error_dialog.dart b/lib/presentation/widgets/auth_error_dialog.dart new file mode 100644 index 0000000..62d3a26 --- /dev/null +++ b/lib/presentation/widgets/auth_error_dialog.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +import '../../configs/configs.dart'; +import '../../core/core.dart'; + +Future showAuthErrorDialog(BuildContext context) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Dialog( + child: Container( + height: AppDimensions.normalize(50), + padding: Space.all(1, .5), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Error', + style: AppText.b1b, + ), + Space.yf(.5), + Text( + 'Try Again!', + style: AppText.b1, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text( + 'Dismiss', + style: AppText.h3b?.copyWith( + color: AppColors.primary, + ), + ), + ) + ], + ) + ], + ), + ), + ), + ); + }); +} diff --git a/lib/presentation/widgets/bottom_navbar.dart b/lib/presentation/widgets/bottom_navbar.dart new file mode 100644 index 0000000..5d7797a --- /dev/null +++ b/lib/presentation/widgets/bottom_navbar.dart @@ -0,0 +1,53 @@ +// bottom_navigation.dart + +import 'package:cargo/core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../application/application.dart'; +import '../../configs/configs.dart'; + +class BottomNavigation extends StatelessWidget { + const BottomNavigation({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, activeTab) { + return SizedBox( + height: AppDimensions.normalize(27), + child: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: activeTab.index, + onTap: (index) { + final newTab = NavigationTab.values[index]; + context.read().updateTab(newTab); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.list_alt_outlined), + label: 'Sargytlarym', + ), + BottomNavigationBarItem( + icon: Icon(Icons.history_rounded), + label: 'Sargytlaryň taryhy', + ), + BottomNavigationBarItem( + icon: Icon(Icons.person_outline_rounded), + label: 'Şahsy otagym', + ), + ], + selectedItemColor: AppColors.primary, + // unselectedItemColor: Colors.white, + iconSize: AppDimensions.normalize(12), + selectedLabelStyle: AppText.b2b, + unselectedLabelStyle: AppText.b2!.copyWith( + color: const Color(0xFF96969C), + ), + backgroundColor: AppColors.surface, + ), + ); + }, + ); + } +} diff --git a/lib/presentation/widgets/credential_failure_dialog.dart b/lib/presentation/widgets/credential_failure_dialog.dart new file mode 100644 index 0000000..53f7e26 --- /dev/null +++ b/lib/presentation/widgets/credential_failure_dialog.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +import '../../configs/configs.dart'; +import '../../core/core.dart'; + +Future showCredentialErrorDialog(BuildContext context) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Dialog( + child: Container( + height: AppDimensions.normalize(60), + padding: Space.all(1, .5), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Username/Password Wrong!', + style: AppText.b1b, + ), + Space.yf(.5), + Text( + 'Try Again!', + style: AppText.b1, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text( + 'Dismiss', + style: AppText.h3b?.copyWith( + color: AppColors.primary, + ), + )) + ], + ) + ], + ), + ), + ), + ); + }); +} diff --git a/lib/presentation/widgets/order_header.dart b/lib/presentation/widgets/order_header.dart new file mode 100644 index 0000000..bdcc046 --- /dev/null +++ b/lib/presentation/widgets/order_header.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../../configs/configs.dart'; +import '../../core/core.dart'; + +class OrderHeader extends StatelessWidget { + const OrderHeader({super.key}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: AppDimensions.normalize(80), + child: Stack( + children: [ + Image.asset( + AppAssets.Header, + fit: BoxFit.fill, + ), + Positioned( + top: 0, + bottom: 0, + left: AppDimensions.normalize(12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Cargo goşundy', + style: AppText.h1b?.copyWith( + color: Colors.white, + ), + ), + Space.yf(0.30), + Text( + 'Öz sargydyňyzy yzarlaň', + style: AppText.b1?.copyWith( + color: AppColors.yellow, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/successful_auth_dialog.dart b/lib/presentation/widgets/successful_auth_dialog.dart new file mode 100644 index 0000000..4fbc40b --- /dev/null +++ b/lib/presentation/widgets/successful_auth_dialog.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../configs/configs.dart'; +import '../../core/core.dart'; + +Future showSuccessfulAuthDialog(BuildContext context, String text) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Dialog( + child: Container( + height: AppDimensions.normalize(77), + padding: Space.all(1, 1.05), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'SUCCESSFULLY ${text.toUpperCase()}', + style: AppText.b1b, + ), + Space.yf(.6), + Text( + 'Congratulations,\nYour Account Has Been Successfully $text!', + style: AppText.b1?.copyWith(height: 1.5), + ), + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // TextButton( + // onPressed: () { + // context.read().add(const GetCart()); + // context.read().fetchDeliveryInfo(); + // Navigator.of(context).pushNamedAndRemoveUntil( + // AppRouter.root, + // ModalRoute.withName(''), + // ); + // }, + // child: Text( + // 'Ok', + // style: AppText.h3b?.copyWith(color: AppColors.CommonCyan), + // ), + // ) + // ], + // ) + ], + ), + ), + ), + ); + }, + ); +} diff --git a/lib/presentation/widgets/widgets.dart b/lib/presentation/widgets/widgets.dart index cb85cd4..53d08c4 100644 --- a/lib/presentation/widgets/widgets.dart +++ b/lib/presentation/widgets/widgets.dart @@ -1 +1,6 @@ export 'button.dart'; +export 'successful_auth_dialog.dart'; +export 'credential_failure_dialog.dart'; +export 'auth_error_dialog.dart'; +export 'bottom_navbar.dart'; +export 'order_header.dart';