Flutter上传页面发布时Firebase无认证令牌报错求助
问题描述
开发Flutter上传页面时,已实现相机/图库选图功能,但点击发布时控制台出现以下警告:
E/StorageUtil(13988): error getting token java.util.concurrent.ExecutionException: com.google.firebase.internal.api.FirebaseNoSignedInUserException: Please sign in before trying to get a token.
W/NetworkRequest(13988): no auth token for request
W/NetworkRequest(13988): No App Check token for request.
上传页面代码如下:
import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:image/image.dart' as im; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:social_appify/pages/home.dart'; import 'package:social_appify/widgets/progress.dart'; import 'package:uuid/uuid.dart'; import '../model/user.dart'; class UploadScreen extends StatefulWidget { final User? currentUser; const UploadScreen({super.key, required this.currentUser}); @override State<UploadScreen> createState() => _UploadScreenState(); } class _UploadScreenState extends State<UploadScreen> { TextEditingController locationController = TextEditingController(); TextEditingController captionController = TextEditingController(); XFile? imageFile; bool isUploading = false; String postId = const Uuid().v4(); handleTakePhoto() async { Navigator.pop(context); imageFile = await ImagePicker().pickImage( source: ImageSource.camera, maxHeight: 675, maxWidth: 960, ); setState(() { imageFile = imageFile; }); } handleImageFromGallery() async { Navigator.pop(context); imageFile = await ImagePicker().pickImage(source: ImageSource.gallery); setState(() { imageFile = imageFile; }); } selectImage(parentContext) { return showDialog( context: parentContext, builder: (context) { return SimpleDialog( title: const Text('Create Post'), children: [ SimpleDialogOption( onPressed: handleTakePhoto, child: const Text('Photo with Camera'), ), SimpleDialogOption( onPressed: handleImageFromGallery, child: const Text('Image from Gallery'), ), SimpleDialogOption( child: const Text('Cancel'), onPressed: () => Navigator.pop(context), ), ], ); }); } Container buildSplash() { return Container( color: Theme.of(context).colorScheme.secondary.withOpacity(0.6), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset( 'assets/images/upload.svg', height: 260.0, ), Padding( padding: const EdgeInsets.only(top: 20.0), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.deepOrange, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), ), onPressed: () => selectImage(context), child: const Text( "Upload Image", style: TextStyle(color: Colors.white, fontSize: 22.0), ), ), ), ], ), ); } clearImage() { setState(() { imageFile = null; }); } compressImage() async { final tempDir = await getTemporaryDirectory(); final pathe = tempDir.path; im.Image? imgFile = im.decodeImage(await imageFile!.readAsBytes()); final compressedImageFile = File('$pathe/img_$postId.jpg') ..writeAsBytesSync(im.encodeJpg(imgFile!, quality: 85)); setState(() { imageFile = XFile(compressedImageFile.path); }); } uploadImage(XFile? imageFile) async { UploadTask uploadTask = reference.child("post_$postId.jpg").putFile(File(imageFile!.path)); TaskSnapshot storageSnapshot = await uploadTask; String downloadUrl = await storageSnapshot.ref.getDownloadURL(); return downloadUrl; } createPostInFirestore( {String? mediaUrl, String? location, String? description}) { postsRef .doc(widget.currentUser!.id) .collection("userPosts") .doc(postId) .set({ "postId": postId, "ownerId": widget.currentUser!.id, "username": widget.currentUser!.username, "mediaUrl": mediaUrl, "description": description, "location": location, "timestamp": timeStamp, "likes": {}, }); captionController.clear(); locationController.clear(); setState(() { imageFile = null; isUploading = false; }); } handleSubmitPost() async { setState(() { isUploading = true; }); await compressImage(); String mediaUrl = await uploadImage(imageFile); createPostInFirestore( mediaUrl: mediaUrl, location: locationController.text, description: captionController.text, ); } Scaffold buildUploadForm() { return Scaffold( appBar: AppBar( backgroundColor: Colors.white70, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: clearImage, ), title: const Text( 'Caption Post', style: TextStyle(color: Colors.black), ), actions: [ TextButton( onPressed: () {}, // isUploading ? null : () => handleSubmitPost(), child: const Text( 'Post', style: TextStyle( color: Colors.blueAccent, fontWeight: FontWeight.bold, fontSize: 20.0), ), ), ], ), body: ListView( children: [ isUploading ? linearProgress() : const Text(""), SizedBox( height: 220, width: MediaQuery.of(context).size.width * 0.8, child: Center( child: AspectRatio( aspectRatio: 16 / 9, child: Container( decoration: BoxDecoration( image: DecorationImage( image: FileImage(File(imageFile!.path)), fit: BoxFit.cover, ), ), ), ), ), ), const Padding( padding: EdgeInsets.only(top: 10.0), ), ListTile( leading: CircleAvatar( backgroundImage: CachedNetworkImageProvider(widget.currentUser!.photoUrl), ), title: SizedBox( width: 250.0, child: TextField( controller: captionController, decoration: const InputDecoration( hintText: "Write a caption...", border: InputBorder.none, ), ), ), ), const Divider(), ListTile( leading: const Icon( Icons.pin_drop, color: Colors.orange, size: 35.0, ), title: SizedBox( width: 250, child: TextField( controller: locationController, decoration: const InputDecoration( hintText: 'Where was this photo taken?', border: InputBorder.none, ), ), ), ), Container( width: 200, height: 100.0, alignment: Alignment.center, child: ElevatedButton.icon( onPressed: () {}, icon: const Icon( Icons.my_location, color: Colors.white, ), label: const Text( 'User Current Location', style: TextStyle(color: Colors.white), ), ), ) ], ), ); } @override Widget build(BuildContext context) { return imageFile == null ? buildSplash() : buildUploadForm(); } }
问题分析
- 核心原因:Firebase Storage和Firestore操作需要用户认证令牌,但当前没有已登录的Firebase用户,导致无法获取有效令牌完成请求。
- 代码隐患:页面接收了
currentUser参数,但未验证该用户是否通过Firebase Auth登录;Post按钮的点击事件被注释,实际无法触发上传流程;初始化或其他环节已尝试调用需要认证的Firebase操作,触发警告。
解决方案
1. 前置验证用户登录状态
在导航到UploadScreen前,检查Firebase Auth当前用户状态,仅允许登录用户访问:
// 导航前的检查逻辑 final firebaseUser = FirebaseAuth.instance.currentUser; if (firebaseUser == null) { // 跳转到登录页面 Navigator.push(context, MaterialPageRoute(builder: (_) => LoginScreen())); } else { // 传入当前用户模型 Navigator.push(context, MaterialPageRoute(builder: (_) => UploadScreen(currentUser: yourUserModel))); }
2. 上传流程中添加认证检查
修改handleSubmitPost方法,先验证用户登录状态:
handleSubmitPost() async { final firebaseUser = FirebaseAuth.instance.currentUser; if (firebaseUser == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请先登录')), ); setState(() => isUploading = false); return; } setState(() { isUploading = true; }); await compressImage(); String mediaUrl = await uploadImage(imageFile); createPostInFirestore( mediaUrl: mediaUrl, location: locationController.text, description: captionController.text, ); }
3. 修复Post按钮点击事件
恢复buildUploadForm中Post按钮的onPressed逻辑:
TextButton( onPressed: isUploading ? null : handleSubmitPost, child: const Text( 'Post', style: TextStyle( color: Colors.blueAccent, fontWeight: FontWeight.bold, fontSize: 20.0), ), ),
4. 处理App Check警告(可选)
若启用了Firebase App Check,需完成以下配置:
- 在Firebase控制台启用App Check,为Flutter应用配置调试令牌(开发环境)
- 在
main函数中初始化App Check:
// main函数中添加 await Firebase.initializeApp(); await FirebaseAppCheck.instance.activate( webRecaptchaSiteKey: 'YOUR_RECAPTCHA_SITE_KEY', androidProvider: AndroidProvider.debug, // 生产环境切换为AndroidProvider.playIntegrity );
内容的提问来源于stack exchange,提问作者Fred_Wolfe
相关产品推荐
相关产品推荐

