本編
こんにちわ、いせりゅーです。
今回は、個人的に注目しているSupabaseについてです。
色々な記事で、データを取得・削除・追加などのやり方は公式ドキュメントや技術記事に載っておりましたが、自分の投稿だけを取得する方法が探しても見つからなかったので、記事にさせていただきました。
公式サイト
今回話すこと
- SNS系のアプリで自分の投稿だけを取得する方法
それ以外は割愛させていただきます。
Postのデータ
今回は以下のデータのModelにしました。
注意
現時点でのアプリのデータのModelは開発段階のものとなっています。
今後も改善予定です。
型 | スネークケース | 変数名 | 説明 |
---|---|---|---|
int | id | id | ID |
String | food_image | foodImage | 食べ物の画像 |
String | food_name | foodName | 食べ物の名前 |
String | restaurant | restaurant | レストラン名 |
String | comment | comment | コメント |
TimeStamp | created_at | createdAt | 作成時間 |
float8 | lat | lat | 緯度 |
float8 | lng | lng | 経度 |
int | heart | heart | いいね数 |
やりたいこと
- タイムライン画面では、すべての投稿を取得したい。
- プロフィール画面では、自分の投稿を取得したい。
注意
現時点でのアプリのUIは開発段階のものとなっています。
今後も改善予定です。
タイムライン画面 | プロフィール画面 |
---|---|
|
実装していく
全部の投稿を取得したい場合
Supabase.instance.client.from('posts').stream(primaryKey: ['id']);
自分の投稿のみを取得したい場合
自分の投稿(ID)のみを取得したい場合は、stream
の後にeq()
で指定したデータのみを取得するようにします。
final supabase = Supabase.instance.client;
// 自分のアカウントのIDを取得
final userId = supabase.auth.currentUser?.id;
supabase.from('posts').stream(primaryKey: ['id']).eq('user_id', userId);
コードの例
my_profile.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:food_gram_app/component/app_list_view.dart';
import 'package:food_gram_app/screen/edit/edit_screen.dart';
import 'package:food_gram_app/screen/my_profile/my_profile_view_model.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class MyProfileScreen extends ConsumerWidget {
const MyProfileScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(myProfileViewModelProvider());
final supabase = Supabase.instance.client;
final user = supabase.auth.currentUser?.id;
final stream =
supabase.from('posts').stream(primaryKey: ['id']).eq('user_id', user);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(backgroundColor: Colors.white),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: AssetImage(state.image),
radius: 50,
),
Spacer(),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'13',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
Text(
'投稿',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
),
SizedBox(width: 10),
],
),
SizedBox(height: 20),
Text(
state.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'@${state.userName}',
style: TextStyle(
fontSize: 18,
),
),
Text(
state.selfIntroduce,
style: TextStyle(
fontSize: 16,
),
),
],
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EditScreen(),
),
).then((_) {
ref
.read(myProfileViewModelProvider().notifier)
.getProfile();
});
},
child: const Text(
'プロフィールを編集',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
),
onPressed: () {
//TODO シェアする
},
child: const Text(
'プロフィールをシェア',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
],
),
const SizedBox(height: 20),
AppListView(stream: stream),
],
),
);
}
}
app_list_view.dart
import 'package:flutter/material.dart';
import 'package:food_gram_app/model/post.dart';
import 'package:food_gram_app/screen/detail/detail_post_screen.dart';
class AppListView extends StatelessWidget {
const AppListView({
required this.stream,
super.key,
});
final Stream<List<Map<String, dynamic>>>? stream;
@override
Widget build(BuildContext context) {
return Expanded(
child: StreamBuilder<List<Map<String, dynamic>>>(
stream: stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
final data = snapshot.data!;
return GridView.builder(
itemCount: data.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 1,
mainAxisSpacing: 1,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
final post = Post(
id: int.parse(data[index]['id'].toString()),
foodImage: data[index]['food_image'],
foodName: data[index]['food_name'],
restaurant: data[index]['restaurant'],
comment: data[index]['comment'],
createdAt: DateTime.parse(data[index]['created_at']),
lat: double.parse(data[index]['lat'].toString()),
lng: double.parse(data[index]['lng'].toString()),
heart: int.parse(data[index]['heart'].toString()),
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPostScreen(
post: post,
),
),
);
},
child: Container(
width: MediaQuery.of(context).size.width / 3,
height: MediaQuery.of(context).size.width / 3,
color: Colors.blue,
//TODO あとでNetworkImageに変更する
child: Image.network(data[index]['food_image']),
),
);
},
);
},
),
);
}
}
Supabaseを使用する上で参考になる記事