Compare commits

...

2 Commits

Author SHA1 Message Date
jahongireshonqulov
0b562d1dcc feat:theme changing done 2025-11-01 15:37:09 +05:00
jahongireshonqulov
b734fc9ce6 feat:theme changing done 2025-11-01 15:37:05 +05:00
10 changed files with 643 additions and 45 deletions

View File

@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:label="food_delivery_client"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">

View File

@@ -3,6 +3,8 @@ abstract class AppLocaleKeys {
static const String language = 'language';
static const String browseSearchHistory = 'browse-search-history';
static const String token = 'token';
static const String theme = 'theme';
static const String appName = "Felix Eats";

View File

@@ -40,6 +40,8 @@ import '../../feature/auth/presentation/blocs/verify_phone_bloc/verify_phone_blo
as _i224;
import '../../feature/common/presentation/blocs/language_bloc/language_bloc.dart'
as _i942;
import '../../feature/common/presentation/blocs/theme_bloc/theme_bloc.dart'
as _i465;
import '../../feature/main/presentation/blocs/main_bloc/main_bloc.dart'
as _i580;
import '../../feature/onboarding/presentation/blocs/splash_bloc/splash_bloc.dart'
@@ -68,6 +70,9 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i28.SplashBloc>(
() => _i28.SplashBloc(gh<_i321.StorageService>()),
);
gh.factory<_i465.ThemeBloc>(
() => _i465.ThemeBloc(gh<_i321.StorageService>()),
);
gh.lazySingleton<_i361.Dio>(() => dioModule.dio(gh<_i667.DioClient>()));
gh.singleton<_i354.RequestHandlerService>(
() => _i354.RequestHandlerService(gh<_i361.Dio>()),

View File

@@ -8,6 +8,10 @@ class StorageService {
_sharedPreference = await SharedPreferences.getInstance();
}
void setBool({required String key, required bool value}) {
_sharedPreference.setBool(key, value);
}
void setString({required String key, required String value}) {
_sharedPreference.setString(key, value);
}
@@ -25,6 +29,10 @@ class StorageService {
String? getString({required String key}) {
return _sharedPreference.getString(key);
}
bool getBool({required String key}) {
return _sharedPreference.getBool(key) ?? false;
}
}
/*

View File

@@ -0,0 +1,37 @@
import 'package:food_delivery_client/food_delivery_client.dart';
part 'theme_event.dart';
part 'theme_state.dart';
part 'theme_bloc.freezed.dart';
@injectable
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
final StorageService _storageService;
ThemeBloc(this._storageService) : super(const ThemeState()) {
on<_Started>(_onStarted);
on<_Changed>(_onChanged);
}
void _onStarted(_Started event, Emitter<ThemeState> emit) {
final isDark = _storageService.getBool(key: AppLocaleKeys.theme);
if (isDark) {
emit(state.copyWith(themeMode: ThemeMode.dark));
} else {
emit(state.copyWith(themeMode: ThemeMode.light));
}
}
void _onChanged(_Changed event, Emitter<ThemeState> emit) {
if (state.themeMode == ThemeMode.light) {
_storageService.setBool(key: AppLocaleKeys.theme, value: true);
emit(state.copyWith(themeMode: ThemeMode.dark));
} else {
_storageService.setBool(key: AppLocaleKeys.theme, value: false);
emit(state.copyWith(themeMode: ThemeMode.light));
}
}
}

View File

@@ -0,0 +1,501 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'theme_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ThemeEvent {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeEvent);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent()';
}
}
/// @nodoc
class $ThemeEventCopyWith<$Res> {
$ThemeEventCopyWith(ThemeEvent _, $Res Function(ThemeEvent) __);
}
/// Adds pattern-matching-related methods to [ThemeEvent].
extension ThemeEventPatterns on ThemeEvent {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Started value)? started,TResult Function( _Changed value)? changed,required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Started value) started,required TResult Function( _Changed value) changed,}){
final _that = this;
switch (_that) {
case _Started():
return started(_that);case _Changed():
return changed(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Started value)? started,TResult? Function( _Changed value)? changed,}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? started,TResult Function()? changed,required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed();case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() started,required TResult Function() changed,}) {final _that = this;
switch (_that) {
case _Started():
return started();case _Changed():
return changed();case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? started,TResult? Function()? changed,}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed();case _:
return null;
}
}
}
/// @nodoc
class _Started implements ThemeEvent {
const _Started();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Started);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent.started()';
}
}
/// @nodoc
class _Changed implements ThemeEvent {
const _Changed();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Changed);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent.changed()';
}
}
/// @nodoc
mixin _$ThemeState {
ThemeMode get themeMode;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ThemeStateCopyWith<ThemeState> get copyWith => _$ThemeStateCopyWithImpl<ThemeState>(this as ThemeState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeState&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode));
}
@override
int get hashCode => Object.hash(runtimeType,themeMode);
@override
String toString() {
return 'ThemeState(themeMode: $themeMode)';
}
}
/// @nodoc
abstract mixin class $ThemeStateCopyWith<$Res> {
factory $ThemeStateCopyWith(ThemeState value, $Res Function(ThemeState) _then) = _$ThemeStateCopyWithImpl;
@useResult
$Res call({
ThemeMode themeMode
});
}
/// @nodoc
class _$ThemeStateCopyWithImpl<$Res>
implements $ThemeStateCopyWith<$Res> {
_$ThemeStateCopyWithImpl(this._self, this._then);
final ThemeState _self;
final $Res Function(ThemeState) _then;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? themeMode = null,}) {
return _then(_self.copyWith(
themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as ThemeMode,
));
}
}
/// Adds pattern-matching-related methods to [ThemeState].
extension ThemeStatePatterns on ThemeState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ThemeState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ThemeState value) $default,){
final _that = this;
switch (_that) {
case _ThemeState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ThemeState value)? $default,){
final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ThemeMode themeMode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that.themeMode);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ThemeMode themeMode) $default,) {final _that = this;
switch (_that) {
case _ThemeState():
return $default(_that.themeMode);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ThemeMode themeMode)? $default,) {final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that.themeMode);case _:
return null;
}
}
}
/// @nodoc
class _ThemeState implements ThemeState {
const _ThemeState({this.themeMode = ThemeMode.light});
@override@JsonKey() final ThemeMode themeMode;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ThemeStateCopyWith<_ThemeState> get copyWith => __$ThemeStateCopyWithImpl<_ThemeState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ThemeState&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode));
}
@override
int get hashCode => Object.hash(runtimeType,themeMode);
@override
String toString() {
return 'ThemeState(themeMode: $themeMode)';
}
}
/// @nodoc
abstract mixin class _$ThemeStateCopyWith<$Res> implements $ThemeStateCopyWith<$Res> {
factory _$ThemeStateCopyWith(_ThemeState value, $Res Function(_ThemeState) _then) = __$ThemeStateCopyWithImpl;
@override @useResult
$Res call({
ThemeMode themeMode
});
}
/// @nodoc
class __$ThemeStateCopyWithImpl<$Res>
implements _$ThemeStateCopyWith<$Res> {
__$ThemeStateCopyWithImpl(this._self, this._then);
final _ThemeState _self;
final $Res Function(_ThemeState) _then;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? themeMode = null,}) {
return _then(_ThemeState(
themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as ThemeMode,
));
}
}
// dart format on

View File

@@ -0,0 +1,8 @@
part of 'theme_bloc.dart';
@freezed
class ThemeEvent with _$ThemeEvent {
const factory ThemeEvent.started() = _Started;
const factory ThemeEvent.changed() = _Changed;
}

View File

@@ -0,0 +1,7 @@
part of 'theme_bloc.dart';
@freezed
abstract class ThemeState with _$ThemeState {
const factory ThemeState({@Default(ThemeMode.light) ThemeMode themeMode}) =
_ThemeState;
}

View File

@@ -1,21 +1,35 @@
import '../../../../../food_delivery_client.dart';
import '../../../common/presentation/blocs/theme_bloc/theme_bloc.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Home"),
GradientSwitch(value:false, onChanged: (value) {
print(value);
}),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Dark mode"),
GradientSwitch(
value: state.themeMode == ThemeMode.dark,
onChanged: (value) {
context.read<ThemeBloc>().add(ThemeEvent.changed());
},
),
],
).paddingSymmetric(horizontal: 20),
],
),
);
},
);
}
}

View File

@@ -1,5 +1,5 @@
import 'package:food_delivery_client/feature/common/presentation/blocs/theme_bloc/theme_bloc.dart';
import 'package:toastification/toastification.dart';
import 'food_delivery_client.dart';
void main() {
@@ -13,7 +13,13 @@ void main() {
Bloc.observer = AppBlocObserver();
await configureDependencies();
runApp(
BlocProvider(create: (context) => sl<LanguageBloc>(), child: MyApp()),
MultiBlocProvider(
providers: [
BlocProvider(create: (context) => sl<LanguageBloc>()),
BlocProvider(create: (context) => sl<ThemeBloc>()),
],
child: MyApp(),
),
);
},
(error, stack) {
@@ -32,25 +38,31 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
context.read<LanguageBloc>().add(LanguageEvent.started());
context.read<ThemeBloc>().add(ThemeEvent.started());
});
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<LanguageBloc, LanguageState>(
bloc: context.read<LanguageBloc>()..add(LanguageEvent.started()),
builder: (context, state) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, themeState) {
return ToastificationWrapper(
child: MaterialApp.router(
title: AppLocaleKeys.appName,
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: ThemeMode.light,
themeMode: themeState.themeMode,
routerConfig: sl<AppRoutes>().router,
locale: state.currentLocale,
supportedLocales: L10n.locales,
@@ -75,5 +87,7 @@ class _MyAppState extends State<MyApp> {
);
},
);
},
);
}
}