我正在开发Flutter应用程序。有一个已登录的用户想要更改其个人资料图片。我正在使用一个远程MySQL数据库来存储所有用户信息,例如电子邮件,个人资料图片和userID。登录后,将使用共享首选项保存所有用户信息。现在,用户想要更改其个人资料图片并打开屏幕更改个人资料图片(cambiar_foto_perfil.dart)。在该屏幕上,有一个image_picker和一个onPressed操作按钮,用于将新图片上传到远程服务器,以更新远程数据库上的新个人资料图片。
当用户更改个人资料图片时,我正在使用Provider来使整个应用程序保持更新。相同的提供程序类正在更新共享首选项。
这是user_provider.dart:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class UsuarioProvider with ChangeNotifier{
String _imagen;
usuarioProvider(){
_imagen = 'No imagen';
loadPreferences();
}
//getters
String get imagen => _imagen;
//setters
void setimagen(String imagen){
_imagen = imagen;
notifyListeners();
savePreferences();
}
loadPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String imagen = prefs.getString('foto');
if (_imagen != null) setimagen(imagen);
}
savePreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('foto', _imagen);
}
}
我在main.dart中的小部件树的顶层实例化提供程序:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
var email = prefs.getString('email');
print(email);
runApp(
EasyLocalization(
child: MultiProvider(
providers: [
ChangeNotifierProvider(create: (BuildContext context) => ClinicaProvider()),
ChangeNotifierProvider(create: (BuildContext context) => UsuarioProvider()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: email == null || email == '' ? Splash2() : HomeScreen())),
path: "assets/translations",
saveLocale: true,
supportedLocales: [Locale('en', 'EN'), Locale('es', 'ES')],
),
);
}
现在,当用户想要更改其个人资料图片时,将调用类cambiar_foto_perfil.dart。这里有类代码:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_capenergy/classes/user.dart';
import 'package:flutter_capenergy/providers/user_provider.dart';
import 'package:flutter_capenergy/servicios/chech_internet.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'package:easy_localization/easy_localization.dart';
import 'package:toast/toast.dart';
class CambiarFotoPerfil extends StatefulWidget {
@override
_CambiarFotoPerfilState createState() => _CambiarFotoPerfilState();
}
class _CambiarFotoPerfilState extends State<CambiarFotoPerfil> {
File _selectedFile;
bool _inProcess = false;
final _picker = ImagePicker();
String miEmail = "";
String miTel = "";
String miUltimoEmail = "";
String miImagen = "";
String miId = "";
String _textoInfo = "";
bool isLoading = false;
@override
void initState() {
super.initState();
getEmailUsuarioActual();
getTelUsuarioActual();
getImagenUsuarioActual();
getIdUsuarioActual();
getUltimoEmailUsuarioActual();
checkInternet().checkConnection(context, "YouAreConnected".tr().toString(),
"YouAreNotConnected".tr().toString(), "WaitConnection".tr().toString());
}
@override
void dispose() {
super.dispose();
print("run dispose");
}
Future<String> getEmailUsuarioActual() async {
final prefs = await SharedPreferences.getInstance();
miEmail = prefs.getString("email");
setState(() {});
}
Future<String> getTelUsuarioActual() async {
final prefs = await SharedPreferences.getInstance();
miTel = prefs.getString("tel");
setState(() {});
}
Future<String> getUltimoEmailUsuarioActual() async {
final prefs = await SharedPreferences.getInstance();
miUltimoEmail = prefs.getString("ultimo_email");
setState(() {});
}
Future<String> getImagenUsuarioActual() async {
final prefs = await SharedPreferences.getInstance();
miImagen = prefs.getString("foto");
setState(() {});
}
Future<String> getIdUsuarioActual() async {
final prefs = await SharedPreferences.getInstance();
miId = prefs.getString("id");
setState(() {});
}
@override
Widget build(BuildContext context) {
localizationsDelegates:
context.localizationDelegates;
supportedLocales:
context.supportedLocales;
locale:
context.locale;
var usuarioProvider = Provider.of<UsuarioProvider>(context);
//changes profile picture in the database
Future<User> cambiarFotoUsuario() async {
setState(() {
isLoading = true;
});
var url =
"https://app.com/flutter_api/cambiar_foto_usuario.php";
final response =
await http.post(url, body: {"id": miId, "foto": miImagen});
print("respuesta :" + response.body);
print(response.statusCode);
final Map parsed = json.decode(response.body);
setState(() {
isLoading = false;
});
}
final String phpEndPoint =
'https://app.com/administrar/application/admin/usuarios/subir_foto_perfil.php';
//uploads profile picture to the server
void _upload(File file) {
if (file == null) return;
setState(() {
_textoInfo = "Subiendo foto al servidor...";
});
String base64Image = base64Encode(file.readAsBytesSync());
String fileName = file.path.split("/").last;
http.post(phpEndPoint, body: {
"image": base64Image,
"name": fileName,
}).then((res) async {
print(res.statusCode);
setState(() {
_textoInfo = "Foto del perfil actualizada";
miImagen = fileName;
});
// updates provider with new profile image
usuarioProvider.setimagen(fileName);
cambiarFotoUsuario();
}).catchError((err) {
print(err);
});
}
Widget getImageWidget() {
if (_selectedFile != null) {
return Image.file(
_selectedFile,
width: 220,
height: 220,
fit: BoxFit.cover,
);
} else {
return Image.asset(
"assets/images/placeholder.png",
width: 220,
height: 220,
fit: BoxFit.cover,
);
}
}
getImage(ImageSource source) async {
this.setState(() {
_inProcess = true;
});
File image = await ImagePicker.pickImage(source: source);
if (image != null) {
File cropped = await ImageCropper.cropImage(
sourcePath: image.path,
aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),
compressQuality: 100,
maxWidth: 700,
maxHeight: 700,
compressFormat: ImageCompressFormat.jpg,
androidUiSettings: AndroidUiSettings(
toolbarColor: Colors.deepOrange,
toolbarTitle: "RPS Cropper",
statusBarColor: Colors.blueAccent,
backgroundColor: Colors.white,
));
this.setState(() {
_selectedFile = cropped;
print(_selectedFile.toString());
_inProcess = false;
});
} else {
this.setState(() {
_inProcess = false;
});
}
}
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Capenergy",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Visibility(
visible: true,
child: Text(
miEmail,
style: TextStyle(
fontSize: 12.0,
),
),
),
],
),
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
radius: 25.0,
backgroundImage: NetworkImage(
'https://app.com/administrar/application/admin/usuarios/' +
usuarioProvider.imagen),
backgroundColor: Colors.transparent,
),
)
],
),
body: Stack(
children: <Widget>[
Container(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
SizedBox(height: 20),
Text(
"Selecciona la foto para tu perfil de usuario ",
style: TextStyle(fontSize: 24.0, color: Colors.black45),
),
],
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 60),
getImageWidget(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
onPressed: () {
getImage(ImageSource
.camera); // You enter here what you want the button to do once the user interacts with it
},
icon: Icon(
Icons.add_a_photo_rounded,
color: Colors.green,
size: 62.0,
),
iconSize: 20.0,
),
IconButton(
onPressed: () {
getImage(ImageSource.gallery);
; // You enter here what you want the button to do once the user interacts with it
},
icon: Icon(
Icons.image_search_outlined,
color: Colors.lightBlueAccent,
size: 62.0,
),
iconSize: 20.0,
),
],
),
SizedBox(height: 20),
RaisedButton.icon(
onPressed: () {
_upload(_selectedFile);
print('Button Clicked.');
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0))),
label: Text(
'Selecciona la foto para tu perfil',
style: TextStyle(color: Colors.white),
),
icon: Icon(
Icons.track_changes,
color: Colors.white,
),
textColor: Colors.white,
splashColor: Colors.red,
color: Colors.blueAccent,
),
Text(_textoInfo),
],
),
(_inProcess)
? Container(
color: Colors.white,
height: MediaQuery.of(context).size.height * 0.95,
child: Center(
child: CircularProgressIndicator(),
),
)
: Center()
],
));
}
}
这里是此屏幕的屏幕截图:
如果用户单击“相机”按钮,则他可以拍照,而不是稍后在占位符空间中显示。如果用户单击搜索按钮并从设备的图库中选择图片,则会发生相同的情况,所选图片将显示在占位符空间中。
Once a picture is taken or selected, if the user clicks on the blue button, the picture is uploaded to the server and the remote database is updated. All this functions are working as expected. On the other hand, I would like to update shared preferences and would like that the provider notifies all other tree widgets that the user profile picture has been changed.
But I am gettin this Exception message at the log console, just after the blue button has been clicked:
======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for UsuarioProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<UsuarioProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<UsuarioProvider>
value: Instance of 'UsuarioProvider'
listening to value
The widget which was currently being built when the offending call was made was: HomeScreen
dirty
dependencies: [_InheritedProviderScope<UsuarioProvider>, MediaQuery, _EasyLocalizationProvider, _InheritedProviderScope<ClinicaProvider>]
state: _HomeScreenState#ce28a
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4292:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4307:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:491:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:226:25)
#4 UsuarioProvider.setimagen (package:flutter_capenergy/providers/user_provider.dart:26:5)
...
The UsuarioProvider sending notification was: Instance of 'UsuarioProvider'
====================================================================================================
Please take a look at my code to check the provider class, the provider instantiation, and the provider use in cambiar_foto_perfil.dart to find out what am I doing wrong.
No need to use setState(), use Consumer() or Provider.of to get values of the Model, Consumer() listens and rebuild UI when notifyListeners() is called
To call provider methods
Provider.of<usuarioProvider>(context, listen: false).setimagen("name_of_image");
要获取提供程序的值,请更改您的代码
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
radius: 25.0,
backgroundImage: NetworkImage(
'https://app.com/administrar/application/admin/usuarios/' +
usuarioProvider.imagen),
backgroundColor: Colors.transparent,
),
到
Consumer<usuarioProvider>(
builder: (context, provider, _) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
radius: 25.0,
backgroundImage: NetworkImage(
'https://app.com/administrar/application/admin/usuarios/' +
provider.imagen),
backgroundColor: Colors.transparent,
),
}
)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句