diff --git a/lib/home.dart b/lib/home.dart index b712f5c..90c4dd4 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart'; - +import 'package:ruswipeshare/sell.dart'; +import 'profile_screen_custom.dart'; import 'main_screen.dart'; class HomeScreen extends StatefulWidget { - const HomeScreen({ Key? key }) : super(key: key); + const HomeScreen({Key? key}) : super(key: key); @override _HomeScreenState createState() => _HomeScreenState(); @@ -12,15 +13,16 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { late PersistentTabController _controller; - @override + @override void initState() { super.initState(); _controller = PersistentTabController(); } + List _buildScreens() => [ const MainScreen(), - const MainScreen(), - const MainScreen(), + const SellScreen(), + const ProfileScreenCustom(), const MainScreen(), ]; @@ -59,36 +61,41 @@ class _HomeScreenState extends State { ), ), ]; - + @override Widget build(BuildContext context) { return PersistentTabView( - context, - controller: _controller, - screens: _buildScreens(), - items: _navBarsItems(), - confineInSafeArea: true, - backgroundColor: Colors.white, // Default is Colors.white. - handleAndroidBackButtonPress: true, // Default is true. - resizeToAvoidBottomInset: true, // This needs to be true if you want to move up the screen when keyboard appears. Default is true. - stateManagement: true, // Default is true. - hideNavigationBarWhenKeyboardShows: true, // Recommended to set 'resizeToAvoidBottomInset' as true while using this argument. Default is true. - decoration: NavBarDecoration( - borderRadius: BorderRadius.circular(10.0), - colorBehindNavBar: Colors.white, - ), - popAllScreensOnTapOfSelectedTab: true, - popActionScreens: PopActionScreensType.all, - itemAnimationProperties: const ItemAnimationProperties( // Navigation Bar's items animation properties. - duration: Duration(milliseconds: 200), - curve: Curves.ease, - ), - screenTransitionAnimation: const ScreenTransitionAnimation( // Screen transition animation on change of selected tab. - animateTabTransition: true, - curve: Curves.ease, - duration: Duration(milliseconds: 200), - ), - navBarStyle: NavBarStyle.style13, // Choose the nav bar style with this property. + context, + controller: _controller, + screens: _buildScreens(), + items: _navBarsItems(), + confineInSafeArea: true, + backgroundColor: Colors.white, // Default is Colors.white. + handleAndroidBackButtonPress: true, // Default is true. + resizeToAvoidBottomInset: + true, // This needs to be true if you want to move up the screen when keyboard appears. Default is true. + stateManagement: true, // Default is true. + hideNavigationBarWhenKeyboardShows: + true, // Recommended to set 'resizeToAvoidBottomInset' as true while using this argument. Default is true. + decoration: NavBarDecoration( + borderRadius: BorderRadius.circular(10.0), + colorBehindNavBar: Colors.white, + ), + popAllScreensOnTapOfSelectedTab: true, + popActionScreens: PopActionScreensType.all, + itemAnimationProperties: const ItemAnimationProperties( + // Navigation Bar's items animation properties. + duration: Duration(milliseconds: 200), + curve: Curves.ease, + ), + screenTransitionAnimation: const ScreenTransitionAnimation( + // Screen transition animation on change of selected tab. + animateTabTransition: true, + curve: Curves.ease, + duration: Duration(milliseconds: 200), + ), + navBarStyle: + NavBarStyle.style13, // Choose the nav bar style with this property. ); } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 9b07f7b..cb31596 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,25 +5,25 @@ import 'firebase_options.dart'; import 'auth_gate.dart'; void main() async { - WidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); runApp(const MyApp()); } class MyApp extends StatelessWidget { - - const MyApp({super.key}); - @override - Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData( - primarySwatch: CustomMaterialColor(200,61,61).mdColor, - ), - home: const AuthGate(), - ); - } + const MyApp({super.key}); + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData( + primarySwatch: CustomMaterialColor(200, 61, 61).mdColor, + ), + home: const AuthGate(), + ); + } } + class CustomMaterialColor { final int r; final int g; @@ -46,4 +46,4 @@ class CustomMaterialColor { }; return MaterialColor(Color.fromRGBO(r, g, b, 1).value, color); } -} \ No newline at end of file +} diff --git a/lib/profile_screen_custom.dart b/lib/profile_screen_custom.dart new file mode 100644 index 0000000..d30657d --- /dev/null +++ b/lib/profile_screen_custom.dart @@ -0,0 +1,303 @@ +// Copyright 2022, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: implementation_imports + +import 'package:firebase_auth/firebase_auth.dart' show ActionCodeSettings, FirebaseAuth, FirebaseAuthException, User; +import 'package:flutter/cupertino.dart' hide Title; +import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Title; +import 'package:flutter_credit_card/flutter_credit_card.dart'; +import 'package:flutterfire_ui/auth.dart'; +import 'package:flutterfire_ui/src/auth/widgets/internal/loading_button.dart'; + +import 'package:flutterfire_ui/src/auth/widgets/internal/universal_button.dart'; + +import 'package:flutterfire_ui/src/auth/screens/internal/multi_provider_screen.dart'; + +import 'package:flutterfire_ui/src/auth/widgets/internal/rebuild_scope.dart'; +import 'package:flutterfire_ui/src/auth/widgets/internal/subtitle.dart'; +import 'package:flutterfire_ui/src/auth/widgets/internal/universal_icon_button.dart'; + +class EditButton extends StatelessWidget { + final bool isEditing; + final VoidCallback? onPressed; + + const EditButton({ + Key? key, + required this.isEditing, + this.onPressed, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return UniversalIconButton( + materialIcon: isEditing ? Icons.check : Icons.edit, + cupertinoIcon: isEditing ? CupertinoIcons.check_mark : CupertinoIcons.pen, + color: theme.colorScheme.secondary, + onPressed: () { + onPressed?.call(); + }, + ); + } +} + +class EmailVerificationBadge extends StatefulWidget { + final FirebaseAuth auth; + final ActionCodeSettings? actionCodeSettings; + const EmailVerificationBadge({ + Key? key, + required this.auth, + this.actionCodeSettings, + }) : super(key: key); + + @override + State createState() => _EmailVerificationBadgeState(); +} + +class _EmailVerificationBadgeState extends State { + late final service = EmailVerificationService(widget.auth) + ..addListener(() { + setState(() {}); + }) + ..reload(); + + EmailVerificationState get state => service.state; + + User get user { + return widget.auth.currentUser!; + } + + TargetPlatform get platform { + return Theme.of(context).platform; + } + + @override + Widget build(BuildContext context) { + if (state == EmailVerificationState.dismissed || state == EmailVerificationState.unresolved || state == EmailVerificationState.verified) { + return const SizedBox.shrink(); + } + + return Padding( + padding: const EdgeInsets.only(top: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.yellow, + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Subtitle( + text: state == EmailVerificationState.sent || state == EmailVerificationState.pending ? 'Verification email sent' : 'Email is not verified', + fontWeight: FontWeight.bold, + ), + if (state == EmailVerificationState.pending) ...[ + const SizedBox(height: 8), + const Text( + 'Please check your email and click the link to verify your email address.', + ), + ] + ], + ), + ), + ), + const SizedBox(height: 16), + if (state == EmailVerificationState.pending) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(width: 16), + Wrap( + children: const [ + Text('Log out and log back in to confirm.'), + ], + ), + ], + ) + else + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (state != EmailVerificationState.sent && state != EmailVerificationState.sending) + UniversalButton( + variant: ButtonVariant.text, + color: Theme.of(context).colorScheme.error, + text: 'Dismiss', + onPressed: () { + setState(service.dismiss); + }, + ), + if (state != EmailVerificationState.sent) + LoadingButton( + isLoading: state == EmailVerificationState.sending, + label: 'Send verification email', + onTap: () { + service.sendVerificationEmail( + platform, + widget.actionCodeSettings, + ); + }, + ) + else + UniversalButton( + variant: ButtonVariant.text, + text: 'Ok', + onPressed: () { + setState(service.dismiss); + }, + ) + ], + ) + ], + ), + ); + } +} + +class ProfileScreenCustom extends MultiProviderScreen { + final List children; + final Color? avatarPlaceholderColor; + final ShapeBorder? avatarShape; + final double? avatarSize; + final List? actions; + final AppBar? appBar; + final CupertinoNavigationBar? cupertinoNavigationBar; + final ActionCodeSettings? actionCodeSettings; + final Set? styles; + + const ProfileScreenCustom({ + Key? key, + FirebaseAuth? auth, + List? providerConfigs, + this.avatarPlaceholderColor, + this.avatarShape, + this.avatarSize, + this.children = const [], + this.actions, + this.appBar, + this.cupertinoNavigationBar, + this.actionCodeSettings, + this.styles, + }) : super(key: key, providerConfigs: providerConfigs, auth: auth); + + Future _reauthenticate(BuildContext context) { + return showReauthenticateDialog( + context: context, + providerConfigs: providerConfigs, + auth: auth, + onSignedIn: () => Navigator.of(context).pop(true), + ); + } + + List getLinkedProviders(User user) { + return providerConfigs.where((config) => user.isProviderLinked(config.providerId)).toList(); + } + + List getAvailableProviders(User user) { + return providerConfigs.where((config) => !user.isProviderLinked(config.providerId)).toList(); + } + + @override + Widget build(BuildContext context) { + return FlutterFireUITheme( + styles: styles ?? const {}, + child: Builder(builder: buildPage), + ); + } + + Widget buildPage(BuildContext context) { + final isCupertino = CupertinoUserInterfaceLevel.maybeOf(context) != null; + final providersScopeKey = RebuildScopeKey(); + final emailVerificationScopeKey = RebuildScopeKey(); + + final user = auth.currentUser!; + + final content = Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Align( + child: UserAvatar( + auth: auth, + placeholderColor: Theme.of(context).colorScheme.primary, + shape: avatarShape, + size: avatarSize, + ), + ), + Align(child: EditableUserDisplayName(auth: auth)), + if (!user.emailVerified) ...[ + RebuildScope( + builder: (context) { + if (user.emailVerified) { + return const SizedBox.shrink(); + } + + return EmailVerificationBadge( + auth: auth, + actionCodeSettings: actionCodeSettings, + ); + }, + scopeKey: emailVerificationScopeKey, + ), + ], + ...children, + const SizedBox(height: 300), + Align( + alignment: Alignment.bottomCenter, + child: SignOutButton( + auth: auth, + variant: ButtonVariant.filled, + ), + ), + const SizedBox(height: 8), + ], + ); + final body = Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 500) { + return Expanded( + child: content, + ); + } else { + return content; + } + }, + ), + ), + ); + + Widget child = SafeArea(child: SingleChildScrollView(child: body)); + + if (isCupertino) { + child = CupertinoPageScaffold( + navigationBar: cupertinoNavigationBar, + child: SafeArea( + child: SingleChildScrollView(child: child), + ), + ); + } else { + child = Scaffold( + appBar: appBar, + body: SafeArea( + child: SingleChildScrollView(child: body), + ), + ); + } + + return FlutterFireUIActions( + actions: actions ?? const [], + child: child, + ); + } +} diff --git a/lib/sell.dart b/lib/sell.dart new file mode 100644 index 0000000..f28b608 --- /dev/null +++ b/lib/sell.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +class SellScreen extends StatefulWidget { + const SellScreen({Key? key}) : super(key: key); + + @override + _SellScreenState createState() => _SellScreenState(); +} + +class _SellScreenState extends State { + @override + Widget build(BuildContext context) { + TimeOfDay _time = TimeOfDay.now(); + return Scaffold( + appBar: AppBar( + title: const Text('Sell'), + automaticallyImplyLeading: false, + ), + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.store_mall_directory, color: Colors.red), + const Text('Place'), + ListTile( + title: Text(_time.format(context)), + onTap: () { + Future selectedTime = showTimePicker( + context: context, + initialTime: _time, + ); + setState(() { + selectedTime.then((value) => _time = value!); + _time = TimeOfDay(hour: 10, minute: 00); + }); + }, + ), + LocationDropdown(), + Icon(Icons.access_time, color: Colors.red), + Expanded( + child: const Text('Time'), + ), + Icon(Icons.attach_money, color: Colors.red), + Expanded( + child: const Text('Cost'), + ), + ], + ), + ); + } +} + +const List list = [ + 'Brower', + 'BDH', + 'LDH', + 'Neilson', + 'Woody\'s' +]; + +class LocationDropdown extends StatefulWidget { + const LocationDropdown({super.key}); + + @override + State createState() => _LocationDropdownState(); +} + +class _LocationDropdownState extends State { + String dropdownValue = list.first; + + @override + Widget build(BuildContext context) { + return DropdownButton( + value: dropdownValue, + onChanged: (String? value) { + // This is called when the user selects an item. + setState(() { + dropdownValue = value!; + }); + }, + items: list.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index a661366..b454d87 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -206,6 +206,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_credit_card: + dependency: "direct main" + description: + name: flutter_credit_card + sha256: "0fc71e8bfb0e126d2c4247830c04e2acf2b831161411a361e9fa9dc1cc41e605" + url: "https://pub.dev" + source: hosted + version: "3.0.5" flutter_dotenv: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e12a74b..47a5e4d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: flutter_dotenv: ^5.0.2 persistent_bottom_nav_bar: any geolocator: ^9.0.2 + flutter_credit_card: any dev_dependencies: flutter_test: