diff --git a/assets/fonts/Blaka-Regular.ttf b/assets/fonts/Blaka-Regular.ttf new file mode 100644 index 0000000..4646e02 Binary files /dev/null and b/assets/fonts/Blaka-Regular.ttf differ diff --git a/lib/main.dart b/lib/main.dart index 7b7f5b6..a053bd5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,122 +1,653 @@ import 'package:flutter/material.dart'; +import 'screens/home_screen.dart'; void main() { - runApp(const MyApp()); + runApp(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 THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + title: 'Connection App', + theme: ThemeData(primarySwatch: Colors.blue, fontFamily: 'Roboto'), + home: LandingPage(), + debugShowCheckedModeBanner: false, ); } } -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; - +class LandingPage extends StatefulWidget { @override - State createState() => _MyHomePageState(); + _LandingPageState createState() => _LandingPageState(); } -class _MyHomePageState extends State { - int _counter = 0; +class _LandingPageState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _slideAnimation; + bool _showBottomSheet = false; - void _incrementCounter() { + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: Duration(milliseconds: 800), + vsync: this, + ); + _slideAnimation = Tween(begin: 1.0, end: 0.0).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + void _showGetStartedBottomSheet() { 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++; + _showBottomSheet = true; }); + _animationController.forward(); + } + + void _hideBottomSheet() { + _animationController.reverse().then((_) { + setState(() { + _showBottomSheet = false; + }); + }); + } + + void _navigateToSignIn() { + Navigator.of( + context, + ).push(MaterialPageRoute(builder: (context) => SignInPage())); + } + + void _navigateToHome() { + Navigator.of( + context, + ).push(MaterialPageRoute(builder: (context) => HomeScreen())); } @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( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // 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. - // - // 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). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, + body: Stack( + children: [ + // Background gradient + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFF32B0A5), // Teal + Color(0xFF4600B9), // Purple + ], + stops: [0.0, 0.5], + ), ), - ], - ), + ), + + // Main content + SafeArea( + child: Column( + children: [ + Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Logo + Container( + padding: EdgeInsets.all(40), + child: Text( + 'وصال', + style: TextStyle( + fontSize: 160, + fontWeight: FontWeight.w200, + fontFamily: 'Blaka', + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(2, 2), + blurRadius: 4, + color: Colors.black.withOpacity(0.3), + ), + ], + ), + ), + ), + + SizedBox(height: 60), + + // Subtitle + Text( + 'Connections, Made\nEasier!', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w600, + color: Colors.white, + height: 1.2, + ), + ), + + SizedBox(height: 80), + + // Get Started Button + Container( + width: 280, + height: 56, + child: ElevatedButton( + onPressed: _showGetStartedBottomSheet, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF6A4C93), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28), + ), + elevation: 8, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Get Started', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(width: 8), + Icon(Icons.arrow_forward, size: 20), + ], + ), + ), + ), + + SizedBox(height: 24), + + // Login link + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: _navigateToHome, + child: Text( + 'Skip to Home', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w600, + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + + // Bottom sheet overlay + if (_showBottomSheet) + AnimatedBuilder( + animation: _slideAnimation, + builder: (context, child) { + return Positioned( + bottom: 0, + left: 0, + right: 0, + child: Transform.translate( + offset: Offset(0, _slideAnimation.value * 400), + child: Container( + height: 400, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 20, + offset: Offset(0, -5), + ), + ], + ), + child: Column( + children: [ + // Handle bar + Container( + margin: EdgeInsets.only(top: 12), + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(2), + ), + ), + + // Close button + Align( + alignment: Alignment.topRight, + child: IconButton( + onPressed: _hideBottomSheet, + icon: Icon(Icons.close, color: Colors.grey[600]), + ), + ), + + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Get Started', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + + SizedBox(height: 12), + + Text( + 'Create your account and start connecting with others', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey[600], + ), + ), + + SizedBox(height: 40), + + // Sign Up button + Container( + width: double.infinity, + height: 56, + child: ElevatedButton( + onPressed: () { + // Handle sign up + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text('Sign Up pressed'), + ), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF6A4C93), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 12, + ), + ), + elevation: 2, + ), + child: Text( + 'Sign Up Now', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + SizedBox(height: 16), + + // Login button + Container( + width: double.infinity, + height: 56, + child: OutlinedButton( + onPressed: _navigateToSignIn, + style: OutlinedButton.styleFrom( + foregroundColor: Color(0xFF6A4C93), + side: BorderSide( + color: Color(0xFF6A4C93), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 12, + ), + ), + ), + child: Text( + 'Login', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + }, + ), + ], + ), + ); + } +} + +class SignInPage extends StatefulWidget { + @override + _SignInPageState createState() => _SignInPageState(); +} + +class _SignInPageState extends State { + final _formKey = GlobalKey(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + bool _isPasswordVisible = false; + bool _isLoading = false; + + @override + void dispose() { + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + void _handleSignIn() async { + if (_formKey.currentState!.validate()) { + setState(() { + _isLoading = true; + }); + + // Simulate API call + await Future.delayed(Duration(seconds: 2)); + + setState(() { + _isLoading = false; + }); + + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Sign in successful!'))); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFF32B0A5), Color(0xFF4600B9)], + stops: [0.0, 0.5], + ), + ), + child: SafeArea( + child: Column( + children: [ + // App bar + Padding( + padding: EdgeInsets.all(16), + child: Row( + children: [ + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: Icon(Icons.arrow_back, color: Colors.white), + ), + Text( + 'Sign In', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ], + ), + ), + + // Content + Expanded( + child: Container( + margin: EdgeInsets.all(16), + padding: EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 20, + offset: Offset(0, 5), + ), + ], + ), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 20), + + // Logo + Center( + child: Text( + 'وصال', + style: TextStyle( + fontSize: 48, + fontWeight: FontWeight.w200, + fontFamily: 'Blaka', + color: Color(0xFF6A4C93), + ), + ), + ), + + SizedBox(height: 40), + + // Welcome text + Text( + 'Welcome Back!', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + textAlign: TextAlign.center, + ), + + SizedBox(height: 8), + + Text( + 'Sign in to your account', + style: TextStyle( + fontSize: 16, + color: Colors.grey[600], + ), + textAlign: TextAlign.center, + ), + + SizedBox(height: 40), + + // Email field + TextFormField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + labelText: 'Email', + prefixIcon: Icon(Icons.email_outlined), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Color(0xFF6A4C93)), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your email'; + } + if (!value.contains('@')) { + return 'Please enter a valid email'; + } + return null; + }, + ), + + SizedBox(height: 20), + + // Password field + TextFormField( + controller: _passwordController, + obscureText: !_isPasswordVisible, + decoration: InputDecoration( + labelText: 'Password', + prefixIcon: Icon(Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible + ? Icons.visibility_off + : Icons.visibility, + ), + onPressed: () { + setState(() { + _isPasswordVisible = !_isPasswordVisible; + }); + }, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Color(0xFF6A4C93)), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; + } + if (value.length < 6) { + return 'Password must be at least 6 characters'; + } + return null; + }, + ), + + SizedBox(height: 12), + + // Forgot password + Align( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Forgot password pressed'), + ), + ); + }, + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Color(0xFF6A4C93), + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + SizedBox(height: 30), + + // Sign in button + Container( + height: 56, + child: ElevatedButton( + onPressed: _isLoading ? null : _handleSignIn, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF6A4C93), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 2, + ), + child: _isLoading + ? CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Colors.white, + ), + ) + : Text( + 'Sign In', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + SizedBox(height: 20), + + // Sign up link + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Don't have an account? ", + style: TextStyle(color: Colors.grey[600]), + ), + GestureDetector( + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Sign up pressed')), + ); + }, + child: Text( + 'Sign Up', + style: TextStyle( + color: Color(0xFF6A4C93), + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + + Spacer(), + ], + ), + ), + ), + ), + ], + ), + ), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 78f349e..3b2b4e0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,89 +1,25 @@ name: wesal_app 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: ^3.8.1 -# 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.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: ^5.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/to/resolution-aware-images - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package - - # 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/to/font-from-package + fonts: + - family: Blaka + fonts: + - asset: assets/fonts/Blaka-Regular.ttf + weight: 200 \ No newline at end of file