我不能使我的“头像”图像持久,每次用户关闭或注销页面的默认图像回来,我想保存在手机上,以便显示更快的本地。我可以帮助你提供完整的代码页,但我认为下面两个应该足够了。第一个是图像应该是持久的:
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'),
),
],
),
);
}
}
提前感谢您的帮助,如果您需要更多信息,请不要犹豫,问我:-)
我是新的领域,我不明白为什么图像是不持久的,我认为我已经做了一切必要的,如果你可以给我一个更正,或者如果你有其他的想法,将使我能够推进它将是非常有帮助的,再次感谢你。
我不知道如何着手解决这个问题。
2条答案
按热度按时间eagi6jfj1#
尝试使用https://pub.dev/packages/cached_network_image。它在本地缓存图像,并快速加载图像。
nr9pn0ug2#
你的代码不是最小的,它很大,不完整,无法测试和解决你的问题(一些部分缺失)。
在你的应用中有一些方法并不太好,特别是从State小部件内部更新StatefulWidget(widget.imageUrl)的属性,因为在重新渲染时,这些StatefulWidget属性可能会被父部件覆盖。
不更新widget.property。只需接收它们并将您要更新的单独属性保存在State中即可。
只要改变方法,如果您的问题再次发生,请尝试用最少的代码或在评论中回复的新项目产生这个问题。
下面是一个例子,使用这种方法更新所有的类。