如果数据未显示在屏幕上,请刷新页面
Refresh page if data isn't shown on screen
我的 initState 函数中有一个 Future,它从缓存中获取 jwt 并使用它来获取登录用户的详细信息。 initState函数是:
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
final token = await CacheService().readCache(key: "jwt");
if (token != null) {
await Provider.of<ProfileNotifier>(context, listen: false)
.decodeUserData(
context: context,
token: token,
option: 'home',
);
}
});
}
现在,它确实有效并且我确实获得了数据,但不是在第一个 运行 上。我必须重新加载模拟器或导航到另一个页面,然后返回页面以重建自身并在屏幕上显示数据。我不明白为什么它不显示第一个 运行 本身的数据。
ProfileNotifier class:
class ProfileNotifier extends ChangeNotifier {
final ProfileAPI _profileAPI = ProfileAPI();
final CacheService _cacheService = CacheService();
ProfileModel _profile = ProfileModel(
profileImage: "",
profileName: "",
profileBio: "",
);
AccountModel _account = AccountModel(
userId: "",
userEmail: "",
userPassword: "",
);
ProfileModel get profile => _profile;
AccountModel get account => _account;
Future decodeUserData({
required BuildContext context,
required String token,
required String option,
}) async {
try {
_profileAPI.decodeUserData(token: token).then((value) async {
final Map<String, dynamic> parsedData = await jsonDecode(value);
var userData = parsedData['data'];
if (userData != null) {
List<String>? userProfileData = await _cacheService.readProfileCache(
key: userData['userData']['id'],
);
if (userProfileData == null) {
final isProfileAvailable =
await Provider.of<ProfileNotifier>(context, listen: false)
.getProfile(
context: context,
userEmail: userData['userData']['userEmail'],
);
if (isProfileAvailable is ProfileModel) {
_profile = isProfileAvailable;
} else {
_account = AccountModel(
userId: userData['userData']['id'],
userEmail: userData['userData']['userEmail'],
userPassword: userData['userData']['userPassword'],
);
_profile = ProfileModel(
profileImage: '',
profileName: '',
);
}
if (option != 'profileCreation' && isProfileAvailable == false) {
Navigator.of(context).pushReplacementNamed(ProfileCreationRoute);
}
} else {
_account = AccountModel(
userId: userData['userData']['id'],
userEmail: userData['userData']['userEmail'],
userPassword: userData['userData']['userPassword'],
);
_profile = ProfileModel(
profileName: userProfileData[3],
profileImage: userProfileData[4],
profileBio: userProfileData[5],
);
}
} else {
Navigator.of(context).pushReplacementNamed(AuthRoute);
}
notifyListeners();
});
} catch (e) {
debugPrint('account/profileNotifier decode error: ' + e.toString());
}
}
Future getProfile({
required BuildContext context,
required String userEmail,
}) async {
try {
var getProfileData = await _profileAPI.getProfile(
userEmail: userEmail,
);
final Map<String, dynamic> parsedProfileData =
await jsonDecode(getProfileData);
bool isReceived = parsedProfileData["received"];
dynamic profileData = parsedProfileData["data"];
if (isReceived && profileData != 'Fill some info') {
Map<String, dynamic> data = {
'id': (profileData['account']['id']).toString(),
'userEmail': profileData['account']['userEmail'],
'userPassword': profileData['account']['userPassword'],
'profile': {
'profileName': profileData['profileName'],
'profileImage': profileData['profileImage'],
'profileBio': profileData['profileBio'],
}
};
AccountModel accountModel = AccountModel.fromJson(
map: data,
);
return accountModel;
} else {
return false;
}
} catch (e) {
debugPrint('profileNotifier getProfile error: ' + e.toString());
}
}
Future setProfile({
required String profileName,
required String profileImage,
required String profileBio,
}) async {
_profile.profileName = profileName;
_profile.profileImage = profileImage;
_profile.profileBio = profileBio;
await _cacheService.writeProfileCache(
key: _account.userId,
value: [
_account.userId,
_account.userEmail,
_account.userPassword as String,
profileName,
profileImage,
profileBio,
],
);
notifyListeners();
}
}
缓存服务class:
class CacheService {
Future<String?> readCache({
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
String? cache = await sharedPreferences.getString(key);
return cache;
}
Future<List<String>?> readProfileCache({
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
List<String>? cachedData = await sharedPreferences.getStringList(key);
return cachedData;
}
Future writeCache({required String key, required String value}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.setString(key, value);
}
Future writeProfileCache(
{required String key, required List<String> value}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.setStringList(key, value);
}
Future deleteCache({
required BuildContext context,
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.remove(key).whenComplete(() {
Navigator.of(context).pushReplacementNamed(AuthRoute);
});
}
}
我似乎无法弄清楚这里的问题。请帮忙。
编辑:数据用于在 CircleAvatar 中显示用户的个人资料图像,如下所示:
@override
Widget build(BuildContext context) {
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: false).profile;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Scaffold(
drawer: const ProfileDrawer(),
appBar: AppBar(
backgroundColor: Colors.white,
leading: Row(children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 9),
child: Builder(builder: (BuildContext context) {
return InkWell(
onTap: () => Scaffold.of(context).openDrawer(),
child: CircleAvatar(
maxRadius: 20.0,
backgroundImage: profile.profileImage.isNotEmpty
? NetworkImage(profile.profileImage)
: null,
child: profile.profileImage.isEmpty
? SvgPicture.asset(
'assets/images/profile-default.svg')
: null),
);
}),
), ....
appBar中的这个CircleAvatar是重建页面后才显示的图片。目前页面上除了应用栏没有其他内容。
当我们使用ChangeNotifier
时,它提供了两种访问数据的选项。它们是:
Read
数据 - 您读取了数据,它不充当流或状态,而且只有一次。这就是你在你的情况下所做的。
优势 - 只要数据只需要一次,例如 - 数学计算,你就用这个。
缺点 - 它不监听变化,返回的数据是静态的。
Watch
数据 - 您所需要的。它以状态方式提供数据,无论您在哪里使用 Watch
访问数据,只要基础数据更新,它(或使用数据中的小部件)都会更新,即使来自其他 Screens/Widgets .
优势 - 数据结果是动态的,每当数据更新时小部件都会更新。
缺点 - 在静态数据有效的情况下,这是不必要的,而且它可能会影响依赖于数据的任何操作。
有两种使用方式Read
和Watch
。
包作者提供的正常功能
//For reading the data
var yourData = Provider.of<YourNotifier>(context, listen: false);
//For watching the data
var yourData = Provider.of<ProfileNotifier>(context, listen: true);
BuildContext
作者提供的扩展功能:
//For reading the data
var yourData = context.read<YourNotifier>();
//For watching the data
var yourData = context.watch<YourNotifier>();
所以,您需要做的是:
改变
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: false).profile;
至
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: true).profile;
//Or
ProfileModel profile = context.watch<ProfileNotifier>().profile;
编辑: 此外,考虑到良好的用户体验,您可以使用 bool 标志在加载数据时更新 UI,如果正在加载,则显示 CircularProgressIndicator
.
我的 initState 函数中有一个 Future,它从缓存中获取 jwt 并使用它来获取登录用户的详细信息。 initState函数是:
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
final token = await CacheService().readCache(key: "jwt");
if (token != null) {
await Provider.of<ProfileNotifier>(context, listen: false)
.decodeUserData(
context: context,
token: token,
option: 'home',
);
}
});
}
现在,它确实有效并且我确实获得了数据,但不是在第一个 运行 上。我必须重新加载模拟器或导航到另一个页面,然后返回页面以重建自身并在屏幕上显示数据。我不明白为什么它不显示第一个 运行 本身的数据。
ProfileNotifier class:
class ProfileNotifier extends ChangeNotifier {
final ProfileAPI _profileAPI = ProfileAPI();
final CacheService _cacheService = CacheService();
ProfileModel _profile = ProfileModel(
profileImage: "",
profileName: "",
profileBio: "",
);
AccountModel _account = AccountModel(
userId: "",
userEmail: "",
userPassword: "",
);
ProfileModel get profile => _profile;
AccountModel get account => _account;
Future decodeUserData({
required BuildContext context,
required String token,
required String option,
}) async {
try {
_profileAPI.decodeUserData(token: token).then((value) async {
final Map<String, dynamic> parsedData = await jsonDecode(value);
var userData = parsedData['data'];
if (userData != null) {
List<String>? userProfileData = await _cacheService.readProfileCache(
key: userData['userData']['id'],
);
if (userProfileData == null) {
final isProfileAvailable =
await Provider.of<ProfileNotifier>(context, listen: false)
.getProfile(
context: context,
userEmail: userData['userData']['userEmail'],
);
if (isProfileAvailable is ProfileModel) {
_profile = isProfileAvailable;
} else {
_account = AccountModel(
userId: userData['userData']['id'],
userEmail: userData['userData']['userEmail'],
userPassword: userData['userData']['userPassword'],
);
_profile = ProfileModel(
profileImage: '',
profileName: '',
);
}
if (option != 'profileCreation' && isProfileAvailable == false) {
Navigator.of(context).pushReplacementNamed(ProfileCreationRoute);
}
} else {
_account = AccountModel(
userId: userData['userData']['id'],
userEmail: userData['userData']['userEmail'],
userPassword: userData['userData']['userPassword'],
);
_profile = ProfileModel(
profileName: userProfileData[3],
profileImage: userProfileData[4],
profileBio: userProfileData[5],
);
}
} else {
Navigator.of(context).pushReplacementNamed(AuthRoute);
}
notifyListeners();
});
} catch (e) {
debugPrint('account/profileNotifier decode error: ' + e.toString());
}
}
Future getProfile({
required BuildContext context,
required String userEmail,
}) async {
try {
var getProfileData = await _profileAPI.getProfile(
userEmail: userEmail,
);
final Map<String, dynamic> parsedProfileData =
await jsonDecode(getProfileData);
bool isReceived = parsedProfileData["received"];
dynamic profileData = parsedProfileData["data"];
if (isReceived && profileData != 'Fill some info') {
Map<String, dynamic> data = {
'id': (profileData['account']['id']).toString(),
'userEmail': profileData['account']['userEmail'],
'userPassword': profileData['account']['userPassword'],
'profile': {
'profileName': profileData['profileName'],
'profileImage': profileData['profileImage'],
'profileBio': profileData['profileBio'],
}
};
AccountModel accountModel = AccountModel.fromJson(
map: data,
);
return accountModel;
} else {
return false;
}
} catch (e) {
debugPrint('profileNotifier getProfile error: ' + e.toString());
}
}
Future setProfile({
required String profileName,
required String profileImage,
required String profileBio,
}) async {
_profile.profileName = profileName;
_profile.profileImage = profileImage;
_profile.profileBio = profileBio;
await _cacheService.writeProfileCache(
key: _account.userId,
value: [
_account.userId,
_account.userEmail,
_account.userPassword as String,
profileName,
profileImage,
profileBio,
],
);
notifyListeners();
}
}
缓存服务class:
class CacheService {
Future<String?> readCache({
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
String? cache = await sharedPreferences.getString(key);
return cache;
}
Future<List<String>?> readProfileCache({
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
List<String>? cachedData = await sharedPreferences.getStringList(key);
return cachedData;
}
Future writeCache({required String key, required String value}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.setString(key, value);
}
Future writeProfileCache(
{required String key, required List<String> value}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.setStringList(key, value);
}
Future deleteCache({
required BuildContext context,
required String key,
}) async {
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
await sharedPreferences.remove(key).whenComplete(() {
Navigator.of(context).pushReplacementNamed(AuthRoute);
});
}
}
我似乎无法弄清楚这里的问题。请帮忙。
编辑:数据用于在 CircleAvatar 中显示用户的个人资料图像,如下所示:
@override
Widget build(BuildContext context) {
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: false).profile;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Scaffold(
drawer: const ProfileDrawer(),
appBar: AppBar(
backgroundColor: Colors.white,
leading: Row(children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 9),
child: Builder(builder: (BuildContext context) {
return InkWell(
onTap: () => Scaffold.of(context).openDrawer(),
child: CircleAvatar(
maxRadius: 20.0,
backgroundImage: profile.profileImage.isNotEmpty
? NetworkImage(profile.profileImage)
: null,
child: profile.profileImage.isEmpty
? SvgPicture.asset(
'assets/images/profile-default.svg')
: null),
);
}),
), ....
appBar中的这个CircleAvatar是重建页面后才显示的图片。目前页面上除了应用栏没有其他内容。
当我们使用ChangeNotifier
时,它提供了两种访问数据的选项。它们是:
Read
数据 - 您读取了数据,它不充当流或状态,而且只有一次。这就是你在你的情况下所做的。优势 - 只要数据只需要一次,例如 - 数学计算,你就用这个。
缺点 - 它不监听变化,返回的数据是静态的。
Watch
数据 - 您所需要的。它以状态方式提供数据,无论您在哪里使用Watch
访问数据,只要基础数据更新,它(或使用数据中的小部件)都会更新,即使来自其他 Screens/Widgets .优势 - 数据结果是动态的,每当数据更新时小部件都会更新。
缺点 - 在静态数据有效的情况下,这是不必要的,而且它可能会影响依赖于数据的任何操作。
有两种使用方式Read
和Watch
。
包作者提供的正常功能
//For reading the data var yourData = Provider.of<YourNotifier>(context, listen: false); //For watching the data var yourData = Provider.of<ProfileNotifier>(context, listen: true);
BuildContext
作者提供的扩展功能://For reading the data var yourData = context.read<YourNotifier>(); //For watching the data var yourData = context.watch<YourNotifier>();
所以,您需要做的是:
改变
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: false).profile;
至
ProfileModel profile =
Provider.of<ProfileNotifier>(context, listen: true).profile;
//Or
ProfileModel profile = context.watch<ProfileNotifier>().profile;
编辑: 此外,考虑到良好的用户体验,您可以使用 bool 标志在加载数据时更新 UI,如果正在加载,则显示 CircularProgressIndicator
.