正确使用Flutter Provider

姆瓦斯科

我正在开发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.

pythondev0101

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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何正确设置 Provider?

Flutter 在 BlocConsumer Widget 上方找不到正确的 Provider

如何在 Flutter 中正确扩展自定义 Provider

无法在此 Widget 上方找到正确的 Provider - Flutter

Flutter:如何将 Provider.of 方法正确放置到 StatefulWidget 中,以便可以在我的代码中使用 Dispose()?

Provider 被废弃后使用

NgDocs Provider如何使用?

Flutter GoogleMap Provider Context问题

flutter:即使使用了`provider`,小部件也在重建

使用Flutter Provider软件包时出现问题

何时在Flutter中使用Provider.of <X>与Consumer <X>

Flutter Provider-使用ProxyProvider的循环依赖关系

在flutter中使用provider时,getter在null上被调用

BlocProvider 错误:找不到正确的 Provider<CounterBloc>

即使我测试数据正确,Flutter Provider也不会更新我的小部件?

Flutter 在导航到不同的路由后找不到正确的 Provider<Bloc>

Flutter:在此页面小部件上方找不到正确的Provider <EntryProvider>

错误:在此 DashboardPage 小部件上方找不到正确的 Provider<DashboardBloc> - FLUTTER

在构建期间调用 Flutter Provider setState() 或 markNeedsBuild()

Flutter Provider 重新初始化模型

Flutter Provider:特定目标的notifyListeners()

Flutter Provider 解释:发生异常。ProviderNotFoundException

通过 Future Provider Value 的 Flutter 列表视图

Flutter Provider-ChangeNotifierProxyProvider设置依赖项

Flutter Provider - 小部件未订阅更新

Navigator 上的 Flutter Provider 注入多个对象

Flutter Provider不重建小部件

Flutter Provider.of <MyProvider>(...)返回null

Flutter Provider问题-更改未反映