如何使图像持久化在Flutter页面中以供应用

cbwuti44  于 2023-06-24  发布在  Flutter
关注(0)|答案(2)|浏览(120)

我不能使我的“头像”图像持久,每次用户关闭或注销页面的默认图像回来,我想保存在手机上,以便显示更快的本地。我可以帮助你提供完整的代码页,但我认为下面两个应该足够了。第一个是图像应该是持久的:

type here

import 'dart:ui';
import 'dart:async';

import 'package:LoginFly/services/auth.dart';
import 'package:LoginFly/views/sigin.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_advanced_drawer/flutter_advanced_drawer.dart';
import 'package:path/path.dart' as path;
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:LoginFly/views/avatar.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> setLoggedIn() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setBool('isLoggedIn', true);
}

Future<void> setLoggedOut() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setBool('isLoggedIn', false);
}

// Enregistrer l'URL de l'image
void saveImageUrl(String imageUrl) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  prefs.setString('imageUrl', imageUrl);
}

// Récupérer l'URL de l'image
Future<String?> getImageUrl() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return prefs.getString('imageUrl');
}

const String defaultImageUrl =
    "https://firebasestorage.googleapis.com/v0/b/loginfly-95e0e.appspot.com/o/avatar.png?alt=media&token=1e95ef19-b452-4f76-a8f5-3a7d5406acd7&_gl=1*znzar2*_ga*NDMwMjY1ODk5LjE2ODU3MzI0NzI.*_ga_CW55HF8NVT*MTY4NjQwMjA1MS45LjEuMTY4NjQwMjk4NC4wLjAuMA..";

void main() {
  runApp(MyApp());
}


class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  late String imageUrl;

  @override
  void initState() {
    super.initState();
    getImageUrl().then((value) {
      setState(() {
        imageUrl = value ?? defaultImageUrl;
      });
    });
  }

  // Enregistrer l'URL de l'image
  Future<void> saveImageUrl(String imageUrl) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('imageUrl', imageUrl);
  }

  // Récupérer l'URL de l'image
  Future<String?> getImageUrl() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getString('imageUrl');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChatRooms(imageUrl: imageUrl, pageName: '',),
    );
  }
}


class ChatRooms extends StatefulWidget {
  late final String imageUrl;
  ChatRooms({required this.imageUrl, required String pageName});

  @override
  _ChatRoomsState createState() => _ChatRoomsState();

}

class _ChatRoomsState extends State<ChatRooms> {
  TextEditingController usernameController = TextEditingController();
  final picker = ImagePicker();// Ajoutez cette ligne pour déclarer et initialiser la variable usernameController

  @override
  void initState() {
    super.initState();
    getImageUrl().then((value) {
      setState(() {
        widget.imageUrl = value ?? defaultImageUrl;
      });
    });
  }

  void _handleLogoutPressed() {
    _onLogoutPressed(context);
  }

  void _handleAvatarPressed() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => AvatarPage(
        imageUrl: widget.imageUrl,
        name: usernameController.text,
      )),
    ).then((imageUrl) {
      if (imageUrl != null) {
        setState(() {
          widget.imageUrl = imageUrl;
        });
        saveImageUrl(imageUrl); // Enregistrer l'URL de l'image dans les SharedPreferences
      }
    });
  }




  final _advancedDrawerController = AdvancedDrawerController();
  bool isDrawerOpen = false;
  int _currentIndex = 0;
  bool isLoading = false;
  AuthMethods authMethods = AuthMethods();
  String? _errorMessage;

  var _isObscured;
  final List<IconData> _icons = [
    Icons.home,
    Icons.account_circle_rounded,
    Icons.favorite,
    Icons.settings,
  ];

  final List<String> _titles = [
    'Home',
    'Profile',
    'Favorites',
    'Settings',
  ];

  @override
  Widget build(BuildContext context) {
    return AdvancedDrawer(
      backdrop: Container(
        width: double.infinity,
        height: double.infinity,
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [Colors.deepPurple, Colors.white.withOpacity(0.2)],
          ),
        ),
      ),
      controller: _advancedDrawerController,
      animationCurve: Curves.easeInOut,
      animationDuration: const Duration(milliseconds: 300),
      animateChildDecoration: true,
      rtlOpening: false,
      childDecoration: const BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(20)),
      ),

      drawer: SafeArea(
        child: Container(
          width: 240,
          color: Colors.transparent,
          child: ListView(
            padding: EdgeInsets.zero,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: 150,
                    height: 150,
                    margin: const EdgeInsets.only(
                      top: 55.0,
                      bottom: 55.0,
                    ),
                    child: GestureDetector(
                      onTap: _handleAvatarPressed,
                      child: Container(
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          image: DecorationImage(
                            image: widget.imageUrl.isEmpty || widget.imageUrl == null
                                ? const NetworkImage(defaultImageUrl)
                                : NetworkImage(widget.imageUrl),
                            fit: BoxFit.cover,
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              ),


              ListTile(
                onTap: () {},
                leading: const Icon(Icons.home),
                title: const Text('Home'),
              ),
              ListTile(
                onTap: () {},
                leading: const Icon(Icons.account_circle_rounded),
                title: const Text('Profile'),
              ),
              ListTile(
                onTap: () {},
                leading: const Icon(Icons.favorite),
                title: const Text('Favorites'),
              ),
              ListTile(
                onTap: () {},
                leading: const Icon(Icons.settings),
                title: const Text('Settings'),
              ),
              const Spacer(),
              Container(
                margin: const EdgeInsets.symmetric(
                  vertical: 16.0,
                ),
                child: const Text(
                  'Terms of Service | Privacy Policy',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.white30,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),

      child: Scaffold(
        appBar: PreferredSize(
          preferredSize: const Size.fromHeight(60.0),
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 300),
            decoration: const BoxDecoration(
              borderRadius: BorderRadius.vertical(
                bottom: Radius.circular(20),
              ),
              color: Colors.grey, // Modifier la couleur de fond ici
            ),
            child: AppBar(
              automaticallyImplyLeading: false,
              flexibleSpace: Container(
                decoration: const BoxDecoration(
                  borderRadius: BorderRadius.vertical(
                    bottom: Radius.circular(20),
                  ),
                  color: Colors.grey, // Modifier la couleur de fond ici
                ),
              ),
              backgroundColor: Colors.transparent,
              title: Row(
                children: [
                  Container(
                    height: 50,
                    width: 50,
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(25),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.white70.withOpacity(0.8),
                          blurRadius: 10,
                        ),
                      ],
                      color: Colors.white.withOpacity(0.9),
                    ),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(25),
                      child: BackdropFilter(
                        filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
                        child: GestureDetector(
                          onTap: _handleMenuButtonPressed,
                          child: Image.asset(
                            'assets/images/logo.png',
                            fit: BoxFit.fill,
                          ),
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(width: 10),
                  const Text(
                    'LoginFly',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                      color: Colors.black,
                    ),
                  ),
                ],
              ),
              actions: [
                GestureDetector(
                  onTap: _handleLogoutPressed, // Appeler la méthode _handleLogoutPressed lors du clic sur la flèche de déconnexion
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: const Icon(
                      Icons.exit_to_app_rounded,
                      color: Colors.black,
                      size: 24.0,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),

        body: WillPopScope(
          onWillPop: _onBackPressed,
          child: GestureDetector(
            onHorizontalDragEnd: (DragEndDetails details) {
              if (details.primaryVelocity! > 700) {
                _advancedDrawerController.showDrawer();
              } else if (details.primaryVelocity! < -700) {
                _advancedDrawerController.hideDrawer();
              }
            },

            onHorizontalDragUpdate: (DragUpdateDetails details) {
              if (details.delta.dx > 700) {
                _advancedDrawerController.showDrawer();
              } else if (details.delta.dx < -700) {
                _advancedDrawerController.hideDrawer();
              }
            },
            behavior: HitTestBehavior.translucent,
            child: _getBodyWidget(_currentIndex),
          ),
        ),
        bottomNavigationBar: Container(
          margin: EdgeInsets.only(
              bottom: MediaQuery
                  .of(context)
                  .padding
                  .bottom),
          decoration: BoxDecoration(
            color: Colors.blueGrey,
            borderRadius: const BorderRadius.vertical(
              top: Radius.circular(20),
            ),
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.2),
                blurRadius: 10,
              ),
            ],
          ),
          child: ClipRRect(
            borderRadius: const BorderRadius.vertical(
              top: Radius.circular(30),
            ),
            child: BottomNavigationBar(
              currentIndex: _currentIndex,
              onTap: (index) {
                setState(() {
                  _currentIndex = index;
                });
              },
              backgroundColor: Colors.blueGrey,
              selectedItemColor: Colors.white,
              unselectedItemColor: Colors.white,
              selectedFontSize: 14,
              unselectedFontSize: 14,
              type: BottomNavigationBarType.fixed,
              items: _icons
                  .asMap()
                  .map(
                    (index, icon) =>
                    MapEntry(
                      index,
                      BottomNavigationBarItem(
                        icon: Icon(icon),
                        label: _titles[index],
                      ),
                    ),
              )
                  .values
                  .toList(),
            ),
          ),
        ),
      ),
    );
  }
  void _onLogoutPressed([BuildContext? context]) {
    showDialog(
      context: context ?? this.context, // Utilisez `this.context` comme valeur par défaut si `context` est nul
      builder: (context) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20.0),
        ),
        title: const Text('Confirmation'),
        content: const Text(
          "Voulez-vous vraiment vous déconnecter ?",
          style: TextStyle(
            fontSize: 16,
            color: Colors.black,
          ),
        ),
        actions: [
          ElevatedButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: const Text(
              'Non',
              style: TextStyle(
                fontSize: 16,
                color: Colors.white,
              ),
            ),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop();
              authMethods.signOut();
              setLoggedOut(); // Appel de la fonction pour définir l'état de connexion sur false
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) => const SingIn()),
              );
            },
            child: const Text(
              'Oui',
              style: TextStyle(
                fontSize: 16,
                color: Colors.white,
                // Autres propriétés de style que vous souhaitez appliquer
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _getBodyWidget(int index) {
    switch (index) {
      case 0:
        return const Center(
          child: Text(
            'Contenu de l\'écran Home',
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 1:
        return const Center(
          child: Text(
            'Contenu de l\'écran Profile',
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 2:
        return const Center(
          child: Text(
            'Contenu de l\'écran Favorites',
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 3:
        return const Center(
          child: Text(
            'Contenu de l\'écran Settings',
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      default:
        return Container();
    }
  }

  void _handleMenuButtonPressed() {
    if (isDrawerOpen) {
      _advancedDrawerController.hideDrawer();
    } else {
      _advancedDrawerController.showDrawer();
    }
  }

  Future<bool> _onBackPressed() {
    if (isDrawerOpen) {
      _advancedDrawerController.hideDrawer();
      return Future.value(false);
    }

    return showDialog(
      context: context,
      builder: (context) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20.0),
        ),
        title: const Text('Confirmation'),
        content: const Text(
          "Voulez-vous vraiment vous déconnecter ?",
          style: TextStyle(
            fontSize: 16,
            color: Colors.black,
            // Autres propriétés de style que vous souhaitez appliquer
          ),
        ),
        actions: [
          ElevatedButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: const Text(
              'Non',
              style: TextStyle(
                fontSize: 16,
                color: Colors.white,
                // Autres propriétés de style que vous souhaitez appliquer
              ),
            ),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop(true);
              SystemNavigator.pop(); // Ferme l'application
            },
            child: const Text(
              'Oui',
              style: TextStyle(
                fontSize: 16,
                color: Colors.white,
                // Autres propriétés de style que vous souhaitez appliquer
              ),
            ),
          ),
        ],
      ),
    ).then((value) => value ?? false);
  }
}

而第二个用户选择他的照片,然后该照片被发送到前一页:

type here

import 'dart:io';

import 'package:LoginFly/Widgets/widget.dart';
import 'package:LoginFly/views/chatRooms.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

const String defaultImageUrl =
    "https://firebasestorage.googleapis.com/v0/b/loginfly-95e0e.appspot.com/o/avatar.png?alt=media&token=1e95ef19-b452-4f76-a8f5-3a7d5406acd7&_gl=1*znzar2*_ga*NDMwMjY1ODk5LjE2ODU3MzI0NzI.*_ga_CW55HF8NVT*MTY4NjQwMjA1MS45LjEuMTY4NjQwMjk4NC4wLjAuMA..";

class AvatarPage extends StatefulWidget {
  final String name;
  final String imageUrl;

  AvatarPage({required this.imageUrl, required this.name});

  @override
  _AvatarPageState createState() => _AvatarPageState();
}

class _AvatarPageState extends State<AvatarPage> {
  String imageUrl = '';
  User? currentUser;
  late TextEditingController nameController;

  @override
  void initState() {
    super.initState();
    currentUser = FirebaseAuth.instance.currentUser;
    _loadImageUrlFromFirebase();
    _loadUserName();
    nameController = TextEditingController(text: widget.name);
  }

  @override
  void dispose() {
    nameController.dispose();
    super.dispose();
  }

  TextStyle myTextFieldStyle() {
    return TextStyle(
      color: Colors.black45, // Replace the color with your desired color
    );
  }

  OutlineInputBorder myInputBorder() {
    return OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(20)),
      borderSide: BorderSide(
        color: Colors.orangeAccent,
        width: 3,
      ),
    );
  }

  OutlineInputBorder myFocusBorder() {
    return OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(20)),
      borderSide: BorderSide(
        color: Colors.greenAccent,
        width: 3,
      ),
    );
  }

  Future<void> _loadUserName() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String savedName = prefs.getString('${currentUser!.uid}_userName') ?? '';
    setState(() {
      nameController.text = savedName;
    });
  }

  Future<void> _saveUserName(String name) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString('${currentUser!.uid}_userName', name);
    final user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      try {
        await FirebaseFirestore.instance
            .collection('users')
            .doc(user.uid)
            .set({'name': name}, SetOptions(merge: true));
      } catch (e) {
        // Handle the error when saving user name
      }
    }
  }

  Future<void> _loadImageUrlFromFirebase() async {
    final user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      setState(() {
        imageUrl = user.photoURL ?? defaultImageUrl;
      });
    }
  }

  void navigateToImagePage(String imageUrl) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ChatRooms(
          imageUrl: imageUrl,
          pageName: '',
        ),
      ),
    );
  }

  Future<void> _pickImage(ImageSource source) async {
    final picker = ImagePicker();
    final pickedImage = await picker.getImage(source: source);

    if (pickedImage != null) {
      final storageRef = firebase_storage.FirebaseStorage.instance
          .ref()
          .child('avatars')
          .child('avatar_${currentUser!.uid}.jpg');

      final imageFile = File(pickedImage.path);

      try {
        // Upload the image to Firebase Storage
        await storageRef.putFile(imageFile);

        // Get the download URL of the image
        final downloadURL = await storageRef.getDownloadURL();

        final user = FirebaseAuth.instance.currentUser;
        if (user != null) {
          try {
            await user.updatePhotoURL(downloadURL);
            await user.updateDisplayName(nameController.text); // Update the user's display name
            // Update the 'imageUrl' variable with the downloaded image URL
            setState(() {
              imageUrl = downloadURL;
            });
          } catch (e) {
            // Handle the error when updating the user profile
          }
        }

        // Show a success notification or perform any necessary actions

      } catch (e) {
        // In case of error during image upload
        // Show an error notification or perform any necessary actions
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Error saving image')),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Avatar Page'),
      ),
      body: Column(
        children: [
          Align(
            alignment: Alignment.center,
            child: GestureDetector(
              onTap: () {
                showDialog(
                  context: context,
                  builder: (context) => AlertDialog(
                    title: Text('Change Image'),
                    content: SingleChildScrollView(
                      child: ListBody(
                        children: [
                          GestureDetector(
                            child: Text('Take a photo'),
                            onTap: () {
                              _pickImage(ImageSource.camera);
                              Navigator.of(context).pop();
                            },
                          ),
                          SizedBox(height: 16),
                          GestureDetector(
                            child: Text('Choose from gallery'),
                            onTap: () {
                              _pickImage(ImageSource.gallery);
                              Navigator.of(context).pop();
                            },
                          ),
                        ],
                      ),
                    ),
                  ),
                );
              },
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: 150,
                    height: 150,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                        image: imageUrl.isEmpty || imageUrl == null
                            ? NetworkImage(defaultImageUrl)
                            : NetworkImage(imageUrl),
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
          SizedBox(height: 5),
          Text(
            "Name: ${nameController.text}",
            style: TextStyle(fontSize: 16),
          ),
          Container(
            alignment: Alignment.topCenter,
            child: TextFormField(
              validator: (val) {
                return val!.isEmpty || val.length < 2 ? "Try another name" : null;
              },
              controller: nameController,
              style: myTextFieldStyle(),
              decoration: InputDecoration(
                fillColor: Colors.white.withOpacity(0.3),
                filled: true,
                labelText: "Name",
                prefixIcon: Icon(Icons.people),
                border: myInputBorder(),
                enabledBorder: myInputBorder(),
                focusedBorder: myFocusBorder(),
                contentPadding: EdgeInsets.symmetric(vertical: 0),
              ),
            ),
          ),
          SizedBox(height: 5),
          ElevatedButton(
            onPressed: () async {
              String name = nameController.text;
              await _saveUserName(name);
              // Do something with the saved name...
              navigateToImagePage(imageUrl);
            },
            child: Text('Sauvgarder'),
          ),
        ],
      ),
    );
  }
}

提前感谢您的帮助,如果您需要更多信息,请不要犹豫,问我:-)
我是新的领域,我不明白为什么图像是不持久的,我认为我已经做了一切必要的,如果你可以给我一个更正,或者如果你有其他的想法,将使我能够推进它将是非常有帮助的,再次感谢你。
我不知道如何着手解决这个问题。

eagi6jfj

eagi6jfj1#

尝试使用https://pub.dev/packages/cached_network_image。它在本地缓存图像,并快速加载图像。

nr9pn0ug

nr9pn0ug2#

你的代码不是最小的,它很大,不完整,无法测试和解决你的问题(一些部分缺失)。
在你的应用中有一些方法并不太好,特别是从State小部件内部更新StatefulWidget(widget.imageUrl)的属性,因为在重新渲染时,这些StatefulWidget属性可能会被父部件覆盖。
不更新widget.property。只需接收它们并将您要更新的单独属性保存在State中即可。
只要改变方法,如果您的问题再次发生,请尝试用最少的代码或在评论中回复的新项目产生这个问题。
下面是一个例子,使用这种方法更新所有的类。

class ChatRooms extends StatefulWidget {
 // only receive final properties here, and don't update them.
 // use separate properties inside State<MyWidget>, that may be updated
 // based on the property received from parent
  ChatRooms({ required String pageName});

  @override
  _ChatRoomsState createState() => _ChatRoomsState();

}

class _ChatRoomsState extends State<ChatRooms> {
  TextEditingController usernameController = TextEditingController();
  final picker = ImagePicker();
  String? imageUrl;

  @override
  void initState() {
    super.initState();
    getImageUrl().then((value) {
      setState(() {
        // Don't do this, don't update any widget.property
       // widget.imageUrl = value ?? defaultImageUrl;
       // Do this
       imageUrl = value ?? defaultImageUrl;
      });
    });
  }
}

相关问题