質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.53%
Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

422閲覧

Storageへの画像アップロードで正しい挙動を得たい。

ituking

総合スコア80

Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2023/07/22 17:52

実現したいこと

  • Storageへの画像アップロードで正しい挙動を得たい。

前提

現在起きている事象としてプロフィール編集ページと投稿ページの画像で表示される画像が同じになってしまう事象が発生しており、理想の挙動としてはプロフィール編集ページでアップロードした画像と投稿ページでアップロードした画像はそれぞれ独立した挙動をしてほしいと思っています。

発生している問題・エラーメッセージ

プロフィール編集ページと投稿ページの画像で表示される画像が同じになってしまっている

該当のソースコード

function_utils.dart

1import 'dart:io'; 2 3import 'package:firebase_storage/firebase_storage.dart'; 4import 'package:flutter/foundation.dart'; 5import 'package:flutter/cupertino.dart'; 6import 'package:image_picker/image_picker.dart'; 7import 'package:permission_handler/permission_handler.dart'; 8 9class FunctionUtils { 10 static Future<dynamic> getImageFromGallery(BuildContext context) async { 11 try { 12 ImagePicker picker = ImagePicker(); 13 PermissionStatus permissionStatus = await Permission.photos.request(); 14 if (permissionStatus.isGranted) { 15 final pickedFile = await picker.pickImage(source: ImageSource.gallery); 16 if (pickedFile != null) { 17 return File(pickedFile.path); 18 } else { 19 return null; 20 } 21 } else if (permissionStatus.isDenied || 22 permissionStatus.isPermanentlyDenied) { 23 if (kDebugMode) { 24 print("Access Denied."); 25 } 26 if (context.mounted) { 27 bool isOpened = await showAlertDialog(context); 28 if (isOpened) { 29 permissionStatus = await Permission.photos.request(); 30 if (permissionStatus.isGranted) { 31 final pickedFile = 32 await picker.pickImage(source: ImageSource.gallery); 33 if (pickedFile != null) { 34 return File(pickedFile.path); 35 } else { 36 return null; 37 } 38 } else { 39 return false; 40 } 41 } else { 42 return false; 43 } 44 } 45 } else { 46 if (kDebugMode) { 47 print("Exception occured."); 48 } 49 if (context.mounted) return; 50 showAlertDialog(context); 51 return false; 52 } 53 } catch (e) { 54 if (kDebugMode) { 55 print("$e"); 56 } 57 showAlertDialog(context); 58 return false; 59 } 60 // final pickedFile = await picker.pickImage(source: ImageSource.gallery); 61 // return File(pickedFile!.path); 62 // if (pickedFile != null) { 63 // setState(() { 64 // image = File(pickedFile.path); 65 // }); 66 // } 67 } 68 69 static Future<bool> showAlertDialog(BuildContext context) async { 70 bool isOpened = false; 71 await showCupertinoDialog<bool>( 72 context: context, 73 barrierDismissible: false, 74 builder: (BuildContext context) => CupertinoAlertDialog( 75 title: const Text("Allow access to Photos"), 76 content: const Text( 77 "This will allow you to share the contents of your camera roll and use other functions. Go to Settings and tap Photos."), 78 actions: [ 79 CupertinoDialogAction( 80 isDestructiveAction: true, 81 onPressed: () { 82 Navigator.pop(context, false); 83 }, 84 child: const Text( 85 "Cancel", 86 ), 87 ), 88 CupertinoDialogAction( 89 isDefaultAction: true, 90 onPressed: () { 91 isOpened = true; 92 Navigator.pop(context, true); 93 openAppSettings(); 94 }, 95 child: const Text( 96 "Settings", 97 style: TextStyle( 98 color: CupertinoColors.activeBlue, 99 ), 100 ), 101 ), 102 ], 103 ), 104 ); 105 return isOpened; 106 } 107 108 static Future<String?> uploadProfileImage(String uid, File image) async { 109 final FirebaseStorage storageInstance = FirebaseStorage.instance; 110 final Reference ref = storageInstance.ref().child(uid); 111 112 try { 113 await ref.putFile(image); 114 String downloadUrl = await ref.getDownloadURL(); 115 if (kDebugMode) { 116 print( 117 "Profile image uploaded successfully. Download URL: $downloadUrl"); 118 } 119 return downloadUrl; 120 } catch (e) { 121 if (kDebugMode) { 122 print("Failed to upload profile image: $e"); 123 } 124 return null; 125 } 126 } 127 128 static Future<String> uploadPostImage(String uid, File image) async { 129 final FirebaseStorage storageInstance = FirebaseStorage.instance; 130 final Reference ref = storageInstance.ref().child('post_images').child(uid); 131 await ref.putFile(image); 132 String downloadUrl = await ref.getDownloadURL(); 133 if (kDebugMode) { 134 print("post_image_path: $downloadUrl"); 135 } 136 return downloadUrl; 137 } 138 139 static Future saveImage(File image) async { 140 try {} catch (e) {} 141 } 142} 143

試したこと

現在起きている事象の解決策として、画像の格納先を参照しているコードが同じになっているか、セキュリティルールに間違いなどはないかなどを見ている状況です。現在はさまざまなことを試行錯誤している段階で、プロフィール編集ページで設定した画像の変更がプロフィールアイコンに適用されるというのではなく、投稿されている画像に適用されてしまう事象が発生しており混乱状態です。この場をお借りして有識者の方のお力をお借りしたいです。関連するコードが文字数制限に抵触するので、コメント欄に記載させていただきます。

補足情報(FW/ツールのバージョンなど)

[✓] Flutter (Channel stable, 3.10.5, on macOS 13.4 22F66 darwin-x64,
locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version
32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] VS Code (version 1.79.2)
[✓] Connected device (3 available)
[✓] Network resources

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ituking

2023/07/22 17:55

```home_page.dart import 'package:chat_app/firestore/account_firestore.dart'; import 'package:chat_app/firestore/post_firestore.dart'; import 'package:chat_app/model/account.dart'; import 'package:chat_app/model/post.dart'; import 'package:chat_app/pages/image_zoom_page.dart'; import 'package:chat_app/pages/post_comment_page.dart'; import 'package:chat_app/pages/post_page.dart'; import 'package:chat_app/utils/authentication.dart'; import 'package:chat_app/utils/like_button.dart'; import 'package:chat_app/utils/share_button.dart'; import 'package:chat_app/utils/timeago.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { double padValue = 0; Account myAccount = Authentication.myAccount!; @override Widget build(BuildContext context) { return Scaffold( body: StreamBuilder<QuerySnapshot>( stream: PostFirestore.posts.snapshots(), builder: (context, postSnapshot) { if (postSnapshot.hasData) { List<String> postAccountIds = []; for (var doc in postSnapshot.data!.docs) { Map<String, dynamic> data = doc.data() as Map<String, dynamic>; if (!postAccountIds.contains(data['post_account_id'])) { postAccountIds.add(data['post_account_id']); } } return FutureBuilder<Map<String, Account>?>( future: AccountFirestore.getPostUserMap(postAccountIds), builder: (context, userSnapshot) { if (userSnapshot.hasData && userSnapshot.connectionState == ConnectionState.done) { return ListView.builder( itemCount: postSnapshot.data!.docs.length, itemBuilder: (context, index) { Map<String, dynamic> data = postSnapshot.data!.docs[index].data() as Map<String, dynamic>; Post post = Post( id: postSnapshot.data!.docs[index].id, postImagePath: data['image_path'], postContent: data['content'], postAccountId: data['post_account_id'], postAccount: userSnapshot.data![data['post_account_id']], postTime: data['post_time'], ); if (kDebugMode) { print(post.postImagePath); } if (kDebugMode) { print(post.postContent); } if (kDebugMode) { print(post.postTime); } return Card( child: SizedBox( height: MediaQuery.of(context).size.height * 0.75, child: Column( children: [ ListTile( leading: CircleAvatar( backgroundImage: NetworkImage( myAccount.profileImagePath, ), ), title: Text( post.postAccount?.name ?? "Unknown", style: const TextStyle( fontWeight: FontWeight.bold, color: CupertinoColors.black, ), ), subtitle: Text( post.postTime == null ? "" : createTimeAgoString( post.postTime!.toDate(), ), style: const TextStyle( fontWeight: FontWeight.normal, color: CupertinoColors.systemGrey, ), ), ), Expanded( child: post.postImagePath != null ? GestureDetector( onTap: () { Navigator.push( context, PageRouteBuilder( pageBuilder: ( context, animation, secondaryAnimation, ) => ImageZoomPage( imagePath: post.postImagePath!, ), transitionsBuilder: ( context, animation, secondaryAnimation, child, ) { return const FadeUpwardsPageTransitionsBuilder() .buildTransitions( MaterialPageRoute( builder: (context) => ImageZoomPage( imagePath: post .postImagePath!, ), ), context, animation, secondaryAnimation, child, ); }, ), ); }, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 5.0, ), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular( 24.0), image: DecorationImage( image: NetworkImage( post.postImagePath!), fit: BoxFit.cover, ), ), ), ), ) : Container( height: MediaQuery.of(context) .size .height * 0.3, width: MediaQuery.of(context) .size .width * 0.9, decoration: BoxDecoration( borderRadius: BorderRadius.circular(24.0), color: CupertinoColors.systemGrey, ), child: const Icon( Icons.image, color: CupertinoColors.white, size: 64.0, ), ), ), Padding( padding: const EdgeInsets.symmetric( horizo
ituking

2023/07/22 17:56 編集

horizontal: 8.0, vertical: 25.0, ), child: Container( alignment: Alignment.centerLeft, width: double.infinity, child: Text( post.postContent, style: const TextStyle( fontSize: 16.0, color: CupertinoColors.black, ), ), ), ), const SizedBox( height: 14.0, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Row( children: [ LikeButton(), SizedBox( width: 8.0, ), Text( "Like", style: TextStyle( color: CupertinoColors.black, ), ), ], ), GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const PostCommentPage(), ), ); }, child: const Row( children: [ Icon( Icons.mode_comment_outlined, color: CupertinoColors.black, ), SizedBox( width: 8.0, ), Text( "Comments", style: TextStyle( color: CupertinoColors.black, ), ), ], ), ), Row( children: [ ShareButton( content: post.postContent, ), const SizedBox( width: 8.0, ), const Text( "Share", style: TextStyle( color: CupertinoColors.black, ), ), ], ), ], ), const SizedBox( height: 12.0, ), ], ), ), ); }, ); } else { return Container(); } }); } else { return Container(); } }), floatingActionButton: FloatingActionButton( backgroundColor: CupertinoColors.activeGreen, elevation: 0.0, foregroundColor: CupertinoColors.white, onPressed: () => showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30), ), ), context: context, isScrollControlled: true, builder: (BuildContext context) { return SingleChildScrollView( child: SizedBox( height: MediaQuery.of(context).size.height * 0.85, child: Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), child: const PostPage(), ), ), ); }, ), child: const Icon( CupertinoIcons.add, ), ), ); } } ```
ituking

2023/07/22 17:57

```edit_account_page.dart import 'dart:io'; import 'package:chat_app/firestore/account_firestore.dart'; import 'package:chat_app/model/account.dart'; import 'package:chat_app/pages/login_page.dart'; import 'package:chat_app/utils/authentication.dart'; import 'package:chat_app/utils/function_utils.dart'; import 'package:chat_app/utils/widget_utils.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class EditAccountPage extends StatefulWidget { const EditAccountPage({super.key}); @override State<EditAccountPage> createState() => _EditAccountPageState(); } class _EditAccountPageState extends State<EditAccountPage> { Account myAccount = Authentication.myAccount!; TextEditingController nameController = TextEditingController(); TextEditingController userIdController = TextEditingController(); TextEditingController selfIntroductionController = TextEditingController(); File? image; ImageProvider getImage() { if (image == null) { return NetworkImage(myAccount.profileImagePath); } else { return FileImage(image!); } } @override void initState() { super.initState(); nameController = TextEditingController(text: myAccount.name); userIdController = TextEditingController(text: myAccount.userId); selfIntroductionController = TextEditingController(text: myAccount.selfIntroduction); } @override Widget build(BuildContext context) { return Scaffold( appBar: WidgetUtils.createAppBar("Edit"), body: Center( child: Column( children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.05, ), GestureDetector( onTap: () async { var result = await FunctionUtils.getImageFromGallery(context); if (result != null) { setState( () { image = File(result.path); }, ); } }, child: CircleAvatar( foregroundImage: getImage(), backgroundColor: CupertinoColors.activeGreen, radius: 50, child: const Icon(Icons.add), ), ), SingleChildScrollView( child: Padding( padding: const EdgeInsets.only(top: 20), child: SizedBox( width: MediaQuery.of(context).size.width * 0.7, child: TextField( controller: nameController, decoration: const InputDecoration( hintText: "Name", focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: CupertinoColors.black, width: 2, ), ), errorStyle: TextStyle( color: CupertinoColors.systemRed, ), ), cursorColor: CupertinoColors.black, cursorWidth: 2.0, ), ), ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: SizedBox( width: MediaQuery.of(context).size.width * 0.7, child: TextField( controller: userIdController, decoration: const InputDecoration( hintText: "ID", focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: CupertinoColors.black, width: 2, ), ), errorStyle: TextStyle( color: CupertinoColors.systemRed, ), ), cursorColor: CupertinoColors.black, cursorWidth: 2.0, ), ), ), SizedBox( width: MediaQuery.of(context).size.width * 0.7, child: TextField( controller: selfIntroductionController, decoration: const InputDecoration( hintText: "Self Introduction", focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: CupertinoColors.black, width: 2, ), ), errorStyle: TextStyle( color: CupertinoColors.systemRed, ), ), cursorColor: CupertinoColors.black, cursorWidth: 2.0, ), ), const SizedBox( height: 50, ), ElevatedButton( onPressed: () async { if (nameController.text.isNotEmpty && userIdController.text.isNotEmpty && selfIntroductionController.text.isNotEmpty) { String profileImagePath = myAccount.profileImagePath; if (image != null) { var result = await FunctionUtils.uploadProfileImage( myAccount.id, image!); if (result != null) { profileImagePath = result; } else { if (kDebugMode) { print("Failed to upload profile image."); } return; } } // if (image == null) { // profileImagePath = myAccount.profileImagePath; // } else { // var result = await FunctionUtils.uploadProfileImage( // myAccount.id, image!); // profileImagePath = result!; // } Account updateAccount = Account( id: myAccount.id, name: nameController.text, profileImagePath: profileImagePath, selfIntroduction: selfIntroductionController.text, userId: userIdController.text, ); // Authentication.myAccount = updateAccount; var result = await AccountFirestore.updateUser(updateAccount); if (result == true) { if (!mounted) return; setState(() { myAccount = updateAccount; }); Navigator.pop(context, true); } else { if (kDebugMode) { print("Failed to update account."); } } } }, child: const Text("Update"), ), const SizedBox( height: 50, ), ElevatedButton( onPressed: () { Authentication.signOut(); while (Navigator.canPop(context)) { Navigator.pop(context); } Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const LoginPage(), ), ); }, child: const Text("Logout"), ), const SizedBox( height: 50, ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: CupertinoColors.systemRed, ), onPressed: () { AccountFirestore.deleteUser(myAccount.id); Authentication.deleteAuth(); Authentication.signOut(); while (Navigator.canPop(context)) { Navigator.pop(context); } Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const LoginPage(), ), ); }, child: const Text( "Delete Account", style: TextStyle( color: CupertinoColors.white, ), ), ), ], ), ), ); } } ```
ituking

2023/07/22 17:57

```post_page.dart import 'dart:io'; import 'package:chat_app/firestore/post_firestore.dart'; import 'package:chat_app/model/post.dart'; import 'package:chat_app/utils/authentication.dart'; import 'package:chat_app/utils/function_utils.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class PostPage extends StatefulWidget { const PostPage({super.key}); @override State<PostPage> createState() => _PostPageState(); } class _PostPageState extends State<PostPage> { TextEditingController contentController = TextEditingController(); File? image; String uid = Authentication.myAccount!.id; FocusNode focusNode = FocusNode(); @override void initState() { super.initState(); focusNode.addListener(() { if (!focusNode.hasFocus) { focusNode.requestFocus(); } }); } @override void dispose() { focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, appBar: AppBar( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30), ), ), elevation: 0, iconTheme: const IconThemeData( color: CupertinoColors.black, ), centerTitle: false, title: const Text( "Create post", style: TextStyle( color: CupertinoColors.black, fontWeight: FontWeight.bold, ), ), actions: [ OutlinedButton( onPressed: () async { if (contentController.text.isNotEmpty && image != null) { final downloadUrl = await FunctionUtils.uploadPostImage(uid, image!); Post newPost = Post( postContent: contentController.text, postAccountId: Authentication.myAccount!.id, id: '', postImagePath: downloadUrl, postAccount: Authentication.myAccount!, postTime: Timestamp.now(), ); var result = await PostFirestore.addPost(newPost); if (result == true) { if (!mounted) return; Navigator.pop(context); } } }, child: const Text( "Post", style: TextStyle( color: CupertinoColors.black, fontWeight: FontWeight.bold, fontSize: 20, ), ), ), ], ), body: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Column( children: [ TextField( controller: contentController, maxLines: null, focusNode: focusNode, decoration: const InputDecoration( hintText: "What's on your mind?", border: InputBorder.none, ), cursorColor: CupertinoColors.black, ), const SizedBox( height: 30, ), image == null ? const SizedBox() : ClipRRect( borderRadius: BorderRadius.circular(24.0), child: SizedBox( height: MediaQuery.of(context).size.height * 0.3, width: MediaQuery.of(context).size.width * 0.9, child: Image.file( image!, fit: BoxFit.cover, ), ), ), const Divider(height: 1), ListTile( leading: const Icon(CupertinoIcons.photo_fill), title: const Text("Add a photo"), onTap: () async { var result = await FunctionUtils.getImageFromGallery(context); if (result != null && result is File) { setState( () { image = result; }, ); } }, ), ], ), ), ), ); } } ```
guest

回答1

0

自己解決

それぞれStorageにpost_images、profile_imageというフォルダを作り、そこに格納されるようにしたところ、それぞれに格納できていることが分かりましたので、解決済みといたします。

投稿2023/07/28 08:48

ituking

総合スコア80

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問