diff --git a/lib/config/design_config.dart b/lib/config/design_config.dart index 9188728..c8fca02 100644 --- a/lib/config/design_config.dart +++ b/lib/config/design_config.dart @@ -3,15 +3,17 @@ import 'package:flutter/services.dart'; class DesignConfig { static const backgroundColor = Color(0XFFF8F8FA); + static const Color surfaceColor = Colors.white; static const Color lightPrimaryColor2 = Color(0XFFE6F3FA); static const Color lightPrimaryColor3 = Color(0XFFF5FAFC); static const Color primaryColor = Color(0XFF007EA7); static const Color darkPrimaryColor2 = Color(0XFF1B3C59); + static const Color secontCtaColor = Color(0XFFF5F5F5); static const Color cardBorderColor = Color(0XFFEBEBEB); static const Color borderColor = Color(0XFFE0E0E0); - static const Color hintColor = Color(0XFFE0E0E0); + static const Color hintColor = Color(0XFFBBBBBB); static const Color captionColor = Color(0XFF666666); static const Color lightRedColor = Color(0XFFFFF8F8); @@ -20,22 +22,29 @@ class DesignConfig { primaryColor: primaryColor, colorScheme: lightColorScheme, fontFamily: 'Dana-FA', + appBarTheme: const AppBarTheme( + backgroundColor: backgroundColor, + foregroundColor: darkPrimaryColor2, + ), textTheme: const TextTheme( bodyText1: body1Text, bodyText2: body2Text, caption: captionText, subtitle2: subtitle2Text, subtitle1: subtitle1Text, + headline3: headline3Text, ), scaffoldBackgroundColor: backgroundColor, ); + static final ThemeData darkTheme = ThemeData(); + static const ColorScheme lightColorScheme = ColorScheme( primary: primaryColor, primaryVariant: Color(0XFF1B3C59), secondary: Color(0XFFD61515), secondaryVariant: Color(0XFFA30001), - surface: Colors.white, + surface: surfaceColor, background: backgroundColor, error: Color(0XFFF00505), onPrimary: Colors.white, @@ -48,6 +57,10 @@ class DesignConfig { static const ColorScheme currentColorScheme = lightColorScheme; + static const TextStyle headline3Text = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ); static const TextStyle subtitle1Text = TextStyle( fontSize: 17, fontWeight: FontWeight.w700, diff --git a/lib/main.dart b/lib/main.dart index d173178..3282f6f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,6 +22,8 @@ class Didvan extends StatelessWidget { debugShowCheckedModeBanner: false, title: 'Didvan', theme: DesignConfig.lightTheme, + darkTheme: DesignConfig.darkTheme, + themeMode: ThemeMode.light, onGenerateRoute: (settings) => RouteGenerator.generateRoute(settings), initialRoute: '/', localizationsDelegates: const [ diff --git a/lib/models/view/app_bar_data.dart b/lib/models/view/app_bar_data.dart new file mode 100644 index 0000000..ad8df2f --- /dev/null +++ b/lib/models/view/app_bar_data.dart @@ -0,0 +1,7 @@ +class AppBarData { + final String? title; + final String? subtitle; + final bool hasBack; + + AppBarData({this.title, this.subtitle, this.hasBack = false}); +} diff --git a/lib/pages/authentication/screens/password.dart b/lib/pages/authentication/screens/password.dart index de83b7f..dd11163 100644 --- a/lib/pages/authentication/screens/password.dart +++ b/lib/pages/authentication/screens/password.dart @@ -30,7 +30,7 @@ class PasswordInput extends StatelessWidget { child: DidvanText( 'فراموشی رمز عبور', style: Theme.of(context).textTheme.subtitle2, - color: Theme.of(context).primaryColor, + color: Theme.of(context).colorScheme.primary, ), ), const Spacer(), diff --git a/lib/pages/authentication/screens/phone_number.dart b/lib/pages/authentication/screens/phone_number.dart index fdff2cc..30d01a6 100644 --- a/lib/pages/authentication/screens/phone_number.dart +++ b/lib/pages/authentication/screens/phone_number.dart @@ -48,7 +48,7 @@ class PhoneNumberInput extends StatelessWidget { style: Theme.of(context) .textTheme .caption! - .copyWith(color: Theme.of(context).primaryColor), + .copyWith(color: Theme.of(context).colorScheme.primary), recognizer: TapGestureRecognizer() ..onTap = () { //TODO: Needs implementaion @@ -60,7 +60,7 @@ class PhoneNumberInput extends StatelessWidget { style: Theme.of(context) .textTheme .caption! - .copyWith(color: Theme.of(context).primaryColor), + .copyWith(color: Theme.of(context).colorScheme.primary), recognizer: TapGestureRecognizer() ..onTap = () { //TODO: Needs implementaion diff --git a/lib/pages/authentication/screens/verification.dart b/lib/pages/authentication/screens/verification.dart index 2ef4a59..9b31088 100644 --- a/lib/pages/authentication/screens/verification.dart +++ b/lib/pages/authentication/screens/verification.dart @@ -48,8 +48,8 @@ class Verification extends StatelessWidget { fieldHeight: 48, fieldWidth: 48, inactiveColor: DesignConfig.borderColor, - activeFillColor: Theme.of(context).primaryColorLight, - activeColor: Theme.of(context).primaryColor, + activeFillColor: Theme.of(context).colorScheme.primary, + activeColor: Theme.of(context).colorScheme.primary, borderRadius: DesignConfig.lowBorderRadius, borderWidth: 1, shape: PinCodeFieldShape.box, diff --git a/lib/pages/home/profile/edit_profile/edit_profile.dart b/lib/pages/home/profile/edit_profile/edit_profile.dart index 8f0cfff..06caf69 100644 --- a/lib/pages/home/profile/edit_profile/edit_profile.dart +++ b/lib/pages/home/profile/edit_profile/edit_profile.dart @@ -1,3 +1,11 @@ +import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/pages/home/profile/edit_profile/widgets/profile_photo.dart'; +import 'package:didvan/pages/home/profile/edit_profile/widgets/switch.dart'; +import 'package:didvan/pages/home/profile/widgets/menu_item.dart'; +import 'package:didvan/widgets/didvan/card.dart'; +import 'package:didvan/widgets/didvan/divider.dart'; +import 'package:didvan/widgets/didvan/scaffold.dart'; +import 'package:didvan/widgets/didvan/text_field.dart'; import 'package:flutter/material.dart'; class EditProfile extends StatelessWidget { @@ -5,6 +13,58 @@ class EditProfile extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold(); + return DidvanScaffold( + appBarData: AppBarData(), + slivers: [ + const SliverToBoxAdapter( + child: ProfilePhoto(), + ), + SliverPadding( + padding: const EdgeInsets.all(16), + sliver: SliverToBoxAdapter( + child: DidvanCard( + child: Column( + children: [ + DidvanTextField( + title: 'نام کاربری', + hintText: 'انتخاب نام کاربری (اختیاری)', + onChanged: (value) { + //TODO: Needs implementation + }, + ), + const SizedBox(height: 16), + DidvanTextField( + title: 'ایمیل', + hintText: 'مثال: example@email.com', + onChanged: (value) { + //TODO: Meeds implementaion + }, + ), + const SizedBox(height: 16), + const DidvanTextField( + title: 'موبایل', + enabled: false, + hintText: '09123456789', //TODO: Needs api + ), + const SizedBox(height: 16), + DidvanSwitch( + value: true, //TODO: Needs api + title: 'ورود با اثر انگشت', + onChanged: (value) => { + //TODO: Needs implementaion + }, + ), + const DidvanDivider(), + MenuItem( + title: 'تغییر رمز عبور', + onTap: () {}, + ), + ], + ), + ), + ), + ), + ], + ); } } diff --git a/lib/pages/home/profile/edit_profile/widgets/profile_photo.dart b/lib/pages/home/profile/edit_profile/widgets/profile_photo.dart new file mode 100644 index 0000000..635ac3c --- /dev/null +++ b/lib/pages/home/profile/edit_profile/widgets/profile_photo.dart @@ -0,0 +1,42 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:flutter/material.dart'; + +class ProfilePhoto extends StatelessWidget { + const ProfilePhoto({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + //TODO: Needs implementation + }, + child: Center( + child: Stack( + children: [ + Container( + height: 96, + width: 96, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: DesignConfig.lightPrimaryColor2, + ), + child: const Icon( + DidvanIcons.profile_solid, + size: 60, + color: DesignConfig.darkPrimaryColor2, + ), + ), + const Positioned( + bottom: 4, + right: 4, + child: Icon( + DidvanIcons.camera_regular, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/home/profile/edit_profile/widgets/switch.dart b/lib/pages/home/profile/edit_profile/widgets/switch.dart new file mode 100644 index 0000000..940e1b1 --- /dev/null +++ b/lib/pages/home/profile/edit_profile/widgets/switch.dart @@ -0,0 +1,50 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/pages/home/profile/widgets/menu_item.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class DidvanSwitch extends StatefulWidget { + final bool value; + final String title; + final IconData? icon; + final void Function(bool value) onChanged; + const DidvanSwitch({ + Key? key, + required this.value, + required this.title, + this.icon, + required this.onChanged, + }) : super(key: key); + + @override + _DidvanSwitchState createState() => _DidvanSwitchState(); +} + +class _DidvanSwitchState extends State { + bool _value = false; + + @override + void initState() { + _value = widget.value; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MenuItem( + title: widget.title, + onTap: () => widget.onChanged(_value), + icon: widget.icon, + trailing: CupertinoSwitch( + activeColor: Theme.of(context).colorScheme.primary, + value: _value, + onChanged: (value) { + setState(() { + _value = value; + }); + widget.onChanged(value); + }, + ), + ); + } +} diff --git a/lib/pages/home/profile/profile.dart b/lib/pages/home/profile/profile.dart index 7ddfd0f..f8cc451 100644 --- a/lib/pages/home/profile/profile.dart +++ b/lib/pages/home/profile/profile.dart @@ -33,7 +33,8 @@ class Profile extends StatelessWidget { MenuItem( title: 'ویرایش پروفایل', icon: DidvanIcons.user_edit_regular, - onTap: () => {}, + onTap: () => + Navigator.of(context).pushNamed(Routes.editProfile), ), const DidvanDivider(), MenuItem( @@ -88,7 +89,7 @@ class Profile extends StatelessWidget { const SizedBox(height: 16), DidvanText( 'نسخه نرم‌افزار: آزمایشی', - style: Theme.of(context).textTheme.bodyText2, + style: Theme.of(context).textTheme.caption, ), ], ), diff --git a/lib/pages/home/profile/widgets/menu_item.dart b/lib/pages/home/profile/widgets/menu_item.dart index bfa8542..64a3510 100644 --- a/lib/pages/home/profile/widgets/menu_item.dart +++ b/lib/pages/home/profile/widgets/menu_item.dart @@ -8,6 +8,7 @@ class MenuItem extends StatelessWidget { final IconData? icon; final String? suffix; final VoidCallback onTap; + final Widget? trailing; final Color color; const MenuItem({ Key? key, @@ -16,6 +17,7 @@ class MenuItem extends StatelessWidget { this.suffix, required this.onTap, this.color = DesignConfig.darkPrimaryColor2, + this.trailing, }) : super(key: key); @override @@ -28,11 +30,12 @@ class MenuItem extends StatelessWidget { if (icon != null) const SizedBox(width: 4), DidvanText(title, color: color), const Spacer(), - Icon( - DidvanIcons.angle_left_regular, - size: 18, - color: color, - ), + trailing ?? + Icon( + DidvanIcons.angle_left_regular, + size: 18, + color: color, + ), ], ), ); diff --git a/lib/pages/home/radar/widgets/search_field.dart b/lib/pages/home/radar/widgets/search_field.dart index dae26aa..4d01a04 100644 --- a/lib/pages/home/radar/widgets/search_field.dart +++ b/lib/pages/home/radar/widgets/search_field.dart @@ -40,7 +40,7 @@ class _SearchFieldState extends State { Radius.circular(4), ), borderSide: BorderSide( - color: Theme.of(context).primaryColor, + color: Theme.of(context).colorScheme.primary, ), ), prefixIcon: const Icon( diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart new file mode 100644 index 0000000..d49f51f --- /dev/null +++ b/lib/providers/settings_provider.dart @@ -0,0 +1,13 @@ +import 'package:didvan/providers/core_provider.dart'; +import 'package:flutter/material.dart'; + +class SettingsProvider extends CoreProvier { + ThemeMode _themeMode = ThemeMode.light; + + set themeMode(ThemeMode themeMode) { + _themeMode = themeMode; + notifyListeners(); + } + + ThemeMode get themeMode => _themeMode; +} diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index fe80ea0..ee73a39 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -3,6 +3,7 @@ import 'package:didvan/pages/authentication/authentication.dart'; import 'package:didvan/pages/authentication/authentication_state.dart'; import 'package:didvan/pages/home/home.dart'; import 'package:didvan/pages/home/home_state.dart'; +import 'package:didvan/pages/home/profile/edit_profile/edit_profile.dart'; import 'package:didvan/pages/splash/splash.dart'; import 'package:didvan/pages/splash/splash_state.dart'; import 'package:didvan/routes/routes.dart'; @@ -33,6 +34,10 @@ class RouteGenerator { child: const Home(), ), ); + case Routes.editProfile: + return _createRoute( + const EditProfile(), + ); default: return _errorRoute(); } diff --git a/lib/routes/routes.dart b/lib/routes/routes.dart index 31a76fe..27e2f8f 100644 --- a/lib/routes/routes.dart +++ b/lib/routes/routes.dart @@ -3,4 +3,5 @@ class Routes { static const String home = '/home'; static const String authenticaion = '/authentication'; static const String profile = '/profile'; + static const String editProfile = '/edit-profile'; } diff --git a/lib/widgets/didvan/app_bar.dart b/lib/widgets/didvan/app_bar.dart new file mode 100644 index 0000000..7082b32 --- /dev/null +++ b/lib/widgets/didvan/app_bar.dart @@ -0,0 +1,40 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/widgets/didvan/text.dart'; +import 'package:flutter/material.dart'; + +class DidvanAppBar extends StatelessWidget { + final AppBarData appBarData; + const DidvanAppBar({Key? key, required this.appBarData}) : super(key: key); + + @override + Widget build(BuildContext context) { + final MediaQueryData d = MediaQuery.of(context); + return AnimatedContainer( + duration: DesignConfig.lowAnimationDuration, + child: Padding( + padding: EdgeInsets.only(top: d.padding.top, right: 4, left: 20), + child: Row( + children: [ + IconButton( + onPressed: () => Navigator.of(context).pop(), + color: DesignConfig.darkPrimaryColor2, + icon: const Icon(Icons.arrow_back), + ), + const SizedBox(width: 16), + Column( + children: [ + if (appBarData.title != null) + DidvanText( + appBarData.title!, + style: Theme.of(context).textTheme.headline3, + color: DesignConfig.darkPrimaryColor2, + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/didvan/button.dart b/lib/widgets/didvan/button.dart index ad6dfca..fe90593 100644 --- a/lib/widgets/didvan/button.dart +++ b/lib/widgets/didvan/button.dart @@ -16,7 +16,7 @@ class DidvanButton extends StatelessWidget { borderRadius: DesignConfig.lowBorderRadius, ), height: 48, - color: Theme.of(context).primaryColor, + color: Theme.of(context).colorScheme.primary, onPressed: onPressed, child: _childBuilder(), ), diff --git a/lib/widgets/didvan/divider.dart b/lib/widgets/didvan/divider.dart index 22e2bf0..5199b62 100644 --- a/lib/widgets/didvan/divider.dart +++ b/lib/widgets/didvan/divider.dart @@ -9,7 +9,7 @@ class DidvanDivider extends StatelessWidget { return Container( height: 1, width: double.infinity, - margin: const EdgeInsets.symmetric(vertical: 12), + margin: const EdgeInsets.symmetric(vertical: 16), color: DesignConfig.borderColor, ); } diff --git a/lib/widgets/didvan/scaffold.dart b/lib/widgets/didvan/scaffold.dart new file mode 100644 index 0000000..3836413 --- /dev/null +++ b/lib/widgets/didvan/scaffold.dart @@ -0,0 +1,58 @@ +import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/widgets/didvan/app_bar.dart'; +import 'package:flutter/material.dart'; + +class DidvanScaffold extends StatefulWidget { + final List? slivers; + final AppBarData appBarData; + final bool hasPadding; + const DidvanScaffold({ + Key? key, + this.slivers, + required this.appBarData, + this.hasPadding = true, + }) : super(key: key); + + @override + State createState() => _DidvanScaffoldState(); +} + +class _DidvanScaffoldState extends State { + bool _isElevated = false; + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + _scrollController.addListener(() => _handleAppBarElevation()); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + automaticallyImplyLeading: false, + pinned: true, + flexibleSpace: DidvanAppBar(appBarData: widget.appBarData), + ), + if (widget.slivers != null) ...widget.slivers! + ], + ), + ); + } + + void _handleAppBarElevation() { + if (_scrollController.position.pixels > kToolbarHeight && !_isElevated) { + setState(() { + _isElevated = true; + }); + } + if (_scrollController.position.pixels < kToolbarHeight && _isElevated) { + setState(() { + _isElevated = false; + }); + } + } +} diff --git a/lib/widgets/didvan/text_field.dart b/lib/widgets/didvan/text_field.dart index 9b0af01..081fb08 100644 --- a/lib/widgets/didvan/text_field.dart +++ b/lib/widgets/didvan/text_field.dart @@ -53,7 +53,11 @@ class _DidvanTextFieldState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.title != null) DidvanText(widget.title!), + if (widget.title != null) + DidvanText( + widget.title!, + color: !widget.enabled ? DesignConfig.hintColor : null, + ), if (widget.title != null) const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 12).copyWith( @@ -82,7 +86,7 @@ class _DidvanTextFieldState extends State { hintText: widget.hintText, hintStyle: Theme.of(context) .textTheme - .bodyText1! + .bodyText2! .copyWith(color: DesignConfig.hintColor), ), ), @@ -93,7 +97,7 @@ class _DidvanTextFieldState extends State { Color _borderColor() { if (_focusNode.hasFocus) { - return Theme.of(context).primaryColor; + return Theme.of(context).colorScheme.primary; } else if (_hasError) { return Theme.of(context).colorScheme.error; } @@ -101,9 +105,13 @@ class _DidvanTextFieldState extends State { } Color _fillColor() { + if (!widget.enabled) { + return DesignConfig.secontCtaColor; + } if (_focusNode.hasFocus) { return DesignConfig.lightPrimaryColor3; - } else if (_hasError) { + } + if (_hasError) { return DesignConfig.lightRedColor; } return Theme.of(context).colorScheme.surface;