skip to Main Content

I can’t make my "avatar" image persistent, each time the user closes or logs out of the page the default image comes back, I would like to save it locally on the phone so that the display is faster. I can help you provide the full code page but I think the two below should be enough.
the first where the image should be persistent:

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);
  }
}

and the second where the user chooses his photo which is then transmitted to the previous page :

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'),
          ),
        ],
      ),
    );
  }
}

thank you in advance for your help and if you need more information do not hesitate to ask me 🙂

I am new in the field and I do not understand why the image is not perstant I think I have done everything necessary, if you could offer me a correction or if you have other ideas that would allow me to advance it would be very helpful, thank you again.

I don’t know how to go about solving this problem.

2

Answers


  1. Try using https://pub.dev/packages/cached_network_image. It caches image locally and loads the image fast.

    Login or Signup to reply.
  2. Your code is not minimal, it’s big and incomplete to test and figure out your issue(some parts missing).
    There are some approaches in your app which are not that good, especially updating properties of StatefulWidget(widget.imageUrl) from inside the State widget, because on re-render those StatefulWidget properties may be overridden by the parent widget.
    Don’t update widget.property. Just receive them and keep separate properties that you want to update inside State.
    Just change the approach, if your issues occur again then please try to produce this issue with a NewProject with minimal code or reply in comments.
    Here is the example, update all your classes using this approach.

    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;
          });
        });
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search