Imporoved state control for Profile Screen

This commit is contained in:
Anastasiia Reshetnik
2024-01-29 13:07:12 +03:00
parent 243c628fc4
commit 57471264fe
9 changed files with 204 additions and 136 deletions

View File

@@ -1,98 +1,154 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
"scale" : "2x",
"size" : "20x20"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
"scale" : "3x",
"size" : "20x20"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
},
{
"size" : "58x58",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
"scale" : "2x",
"size" : "29x29"
},
{
"size" : "87x87",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
"scale" : "3x",
"size" : "29x29"
},
{
"size" : "40x40",
"filename" : "Icon-App-20x20@2x.png",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
"scale" : "2x",
"size" : "40x40"
},
{
"size" : "80x80",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
"scale" : "3x",
"size" : "40x40"
},
{
"size" : "120x120",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "",
"scale" : "3x"
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"size" : "76x76",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"size" : "152x152",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"filename" : "Icon-App-20x20@1x.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "Icon-App-40x40@1x.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "Icon-App-29x29@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "58x58"
},
{
"filename" : "Icon-App-40x40@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "80x80"
},
{
"filename" : "Icon-App-29x29@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "87x87"
},
{
"filename" : "Icon-App-40x40@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "120x120"
},
{
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
"idiom" : "ipad",
"scale" : "2x",
"size" : "152x152"
},
{
"size" : "167x167",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
"idiom" : "ipad",
"scale" : "2x",
"size" : "167x167"
},
{
"size" : "1024x1024",
"idiom" : "ipad",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
"idiom" : "ipad",
"scale" : "1x",
"size" : "1024x1024"
},
{
"filename" : "Icon-App-20x20@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60",
"unassigned" : true
}
],
"info" : {
"version" : 1,
"author" : "ios_icon_set"
"author" : "xcode",
"version" : 1
}
}
}

View File

@@ -171,4 +171,4 @@ class AuthScreen extends StatelessWidget {
return errors;
}
}
}

View File

@@ -15,31 +15,8 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
MyProfileBloc(MyProfileState initialState) : super(initialState) {
on<MyProfileInitialEvent>(_onInitialize);
}
Stream<MyProfileState> mapEventToState(MyProfileEvent event) async* {
if (event is LogoutEvent) {
// Удаление токенов из баз данных Hive и Laravel Sanctum
var box = await Hive.openBox('myBox');
final token = box.get('token');
box.delete('token');
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/logout'),
headers: {
'Authorization': 'Bearer $token',
},
);
if (response.statusCode == 200) {
// Токен Laravel Sanctum успешно удален из базы данных
} else {
// Обработка ошибки
}
// Перенаправление пользователя на экран AuthScreen
}
}
_onInitialize(
Future<void> _onInitialize(
MyProfileInitialEvent event,
Emitter<MyProfileState> emit,
) async {
@@ -52,17 +29,35 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
},
);
final data = jsonDecode(response.body);
emit(state.copyWith(
user: data['user'],
userDetails: data['userDetails'],
token: token,
chartPoints: data['chartPoints'],
plans: data['plans'],
chestCircumferenceController: TextEditingController(),
waistCircumferenceController: TextEditingController(),
hipCircumferenceController: TextEditingController(),
weightController: TextEditingController(),
circumferences: data['circumferences'],
));
if (state is MyProfileLoadedState) {
// Если текущее состояние уже загружено, обновляем его
emit((state as MyProfileLoadedState).copyWith(
user: data['user'],
userDetails: data['userDetails'],
token: token,
chartPoints: data['chartPoints'],
plans: data['plans'],
chestCircumferenceController: TextEditingController(),
waistCircumferenceController: TextEditingController(),
hipCircumferenceController: TextEditingController(),
weightController: TextEditingController(),
circumferences: data['circumferences'],
));
} else {
// Если текущее состояние - начальное, создаем новое загруженное состояние
emit(MyProfileLoadedState(
user: data['user'],
userDetails: data['userDetails'],
token: token,
chartPoints: data['chartPoints'],
plans: data['plans'],
chestCircumferenceController: TextEditingController(),
waistCircumferenceController: TextEditingController(),
hipCircumferenceController: TextEditingController(),
weightController: TextEditingController(),
circumferences: data['circumferences'],
));
}
}
}
}

View File

@@ -1,10 +1,21 @@
// ignore_for_file: must_be_immutable
part of 'my_profile_bloc.dart';
/// Represents the state of MyProfile in the application.
class MyProfileState extends Equatable {
MyProfileState(
const MyProfileState({this.myProfileModelObj});
final MyProfileModel? myProfileModelObj;
@override
List<Object?> get props => [myProfileModelObj];
}
class MyProfileInitialState extends MyProfileState {
MyProfileInitialState({required MyProfileModel myProfileModelObj}) : super(myProfileModelObj: myProfileModelObj);
}
/// Represents the state of MyProfile in the application.
class MyProfileLoadedState extends MyProfileState {
MyProfileLoadedState(
{this.chestCircumferenceController,
this.waistCircumferenceController,
this.hipCircumferenceController,
@@ -15,7 +26,7 @@ class MyProfileState extends Equatable {
this.chartPoints,
this.plans,
this.token,
this.circumferences});
this.circumferences}) : super(myProfileModelObj: null);
MyProfileModel? myProfileModelObj;
Map<String, dynamic>? user;
@@ -42,7 +53,7 @@ class MyProfileState extends Equatable {
hipCircumferenceController,
token
];
MyProfileState copyWith({
MyProfileLoadedState copyWith({
MyProfileModel? myProfileModelObj,
Map<String, dynamic>? user,
Map<String, dynamic>? userDetails,
@@ -55,7 +66,7 @@ class MyProfileState extends Equatable {
TextEditingController? hipCircumferenceController,
String? token,
}) {
return MyProfileState(
return MyProfileLoadedState(
myProfileModelObj: myProfileModelObj ?? this.myProfileModelObj,
user: user ?? this.user,
plans: plans ?? this.plans,
@@ -73,3 +84,4 @@ class MyProfileState extends Equatable {
);
}
}

View File

@@ -29,11 +29,25 @@ class MyProfileScreen extends StatelessWidget {
child: MyProfileScreen());
}
@override
Widget build(BuildContext context) {
mediaQueryData = MediaQuery.of(context);
Widget build(BuildContext context) {
return BlocBuilder<MyProfileBloc, MyProfileState>(
builder: (context, state) {
builder: (context, state) {
if (state is MyProfileInitialState) {
// Это начальное состояние, данные ещё не загружены.
return CircularProgressIndicator();
} else if (state is MyProfileLoadedState) {
// Данные загружены, можно строить экран.
return _buildMyProfileScreen(context, state);
} else {
// Другие состояния, если они есть.
return Container(); // Пустой контейнер для необработанных состояний.
}
},
);
}
Widget _buildMyProfileScreen(BuildContext context, MyProfileLoadedState state) {
mediaQueryData = MediaQuery.of(context);
return SafeArea(
child: Scaffold(
appBar: _buildAppBar(
@@ -133,15 +147,6 @@ class MyProfileScreen extends StatelessWidget {
.toList()),
_buildChartLegend()
]));
/*case 4:
return NextStepButton(
buttonStyle:
CustomButtonStyles.outlinePrimaryTL15,
text: "тест".toUpperCase(),
margin: EdgeInsets.only(left: 10.h),
onPressed: () {
print(dotenv.env['STORAGE_URL']!);
});*/
default:
return Text('text',
style: TextStyle(fontSize: 16.0));
@@ -160,7 +165,6 @@ class MyProfileScreen extends StatelessWidget {
SizedBox(height: 5.v),
])),
bottomNavigationBar: _buildBottomBar(context)));
});
}
/// Section Widget
@@ -752,7 +756,7 @@ class MyProfileScreen extends StatelessWidget {
return bmiList;
}
_editCircumference(BuildContext context, MyProfileState state) {
_editCircumference(BuildContext context, MyProfileLoadedState state) {
showDialog(
context: context,
builder: (context) {
@@ -857,7 +861,7 @@ class MyProfileScreen extends StatelessWidget {
);
}
_editWeight(BuildContext context, MyProfileState state) {
_editWeight(BuildContext context, MyProfileLoadedState state) {
showDialog(
context: context,
builder: (context) {

View File

@@ -19,12 +19,25 @@ class MySettingsScreen extends StatelessWidget {
child: MySettingsScreen());
}
@override
Widget build(BuildContext context) {
mediaQueryData = MediaQuery.of(context);
Widget build(BuildContext context) {
return BlocBuilder<MyProfileBloc, MyProfileState>(
builder: (context, state) {
print(state.userDetails);
builder: (context, state) {
if (state is MyProfileInitialState) {
// Это начальное состояние, данные ещё не загружены.
return CircularProgressIndicator();
} else if (state is MyProfileLoadedState) {
// Данные загружены, можно строить экран.
return _buildSetingsScreen(context, state);
} else {
// Другие состояния, если они есть.
return Container(); // Пустой контейнер для необработанных состояний.
}
},
);
}
Widget _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) {
mediaQueryData = MediaQuery.of(context);
final List<String> userDetails = [
state.userDetails!['first_name'],
state.userDetails!['last_name'],
@@ -54,7 +67,6 @@ class MySettingsScreen extends StatelessWidget {
),
),
bottomNavigationBar: _buildBottomBar(context)));
});
}
/// Section Widget

View File

@@ -76,10 +76,10 @@ class AdditionalInformationScreen extends StatelessWidget {
headers: {'Content-Type': 'application/json'},
body: json.encode(sheet),
);
var res = await jsonDecode(response.body);
final box = await Hive.openBox('token');
final token = json.decode(response.body)['token'];
final token = res['token']['plainTextToken'];
box.put('token', token);
print('token: ${box.get('token')}');
Navigator.pushNamed(
context,
AppRoutes.myProfile,

View File

@@ -64,11 +64,11 @@ class _FavoriteFoodsScreenState extends State<FavoriteFoodsScreen> {
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall)),
SizedBox(height: 23.v),
SizedBox(height: 13.v),
_buildTextFormFieldFav(context),
SizedBox(height: 22.v),
SizedBox(height: 12.v),
_buildTextFormFieldUnfav(context),
SizedBox(height: 27.v),
SizedBox(height: 17.v),
BlocBuilder<FavoriteFoodsBloc, FavoriteFoodsState>(
builder: (context, state) {
return NextStepButton(

View File

@@ -83,17 +83,6 @@ class LanguageSelectScreen extends StatelessWidget {
});
}),
Spacer(flex: 63),
NextStepButton(
text: "тест профиля".toUpperCase(),
margin: EdgeInsets.only(
left: 16.h, right: 16.h, bottom: 50.v),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
onPressed: () {
Navigator.pushNamed(
context,
AppRoutes.myProfile,
);
}),
])),
bottomNavigationBar:
BlocBuilder<LanguageSelectBloc, LanguageSelectState>(