LoginSignup
1
0

【Flutter x Supabase】SNS系のアプリで自分の投稿だけを取得する方法

Last updated at Posted at 2023-12-22

本編

こんにちわ、いせりゅーです。
今回は、個人的に注目している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を使用する上で参考になる記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0