こんにちは、株式会社Pentagonでアプリ開発をしている中原です。
マッチングアプリでは、ユーザーが自分の好みに基づいてプロフィールを選ぶマッチング機能があります。この機能は、ユーザーが相手のプロフィールを左右(嫌い、好き)にスワイプ
することで、相手に興味があることを示し、次のプロフィールを表示します。また、スワイプ
操作は簡単で直感的な操作であるため、ユーザーはすぐに理解し利用することができます。これにより、多くのプロフィールから相手を選ぶことが可能となり、マッチングの可能性が向上します。その結果、使いやすさとユーザー体験が向上します。この記事では、そのようなスワイプ
(上下左右)可能なウィジェットの実装方法について説明します。
【こんな人に読んで欲しい】
- Flutterアプリ開発エンジニア。
- マッチング機能のようなスワイプ(上下左右)可能なウィジェットを実装したい方
【この記事を読むメリット】
- スワイプ(上下左右)可能なウィジェットの実装方法を学べます。
【結論】
appinio_swiper
パッケージを追加し、AppinioSwiper()
ウィジェット引数のcardsBuilder
に表示したいウィジェットを指定するだけで、そのウィジェットを上下左右にスワイプ
することが可能となります。また、AppinioSwiper()
ウィジェット引数のcontroller
にAppinioSwiperController
を指定することで、そのウィジェットのスワイプ方向を自由に制御することも可能です。
この記事で触れないこと
この記事では、以下の実装や説明については省略しています。
- Flutterの開発環境構築
- AsyncNotifierProviderの説明
- Freezedの説明
開発環境
- Flutter 3.10.6
- Dart 3.0.6
- vscode
パッケージ
dependencies:
flutter:
sdk: flutter
appinio_swiper: ^2.0.3 // パッケージを追加
flutter_riverpod: ^2.4.0
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.6
freezed: ^2.4.3
json_serializable: ^6.7.1
画像の準備
assets
フォルダを作成します。assets
フォルダの中にimages
フォルダを作成します。- 画像ファイルの
flutter.png
をimages
フォルダに挿入します。画像ファイルにはお好きな画像を設定してください。 pubspec.yaml
に以下の設定を追加します。
flutter:
uses-material-design: true
// 追加
assets:
- assets/images/
Userモデルの実装
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
@freezed
class User with _$User {
const factory User({
@Default([]) List<String> profileImageURL,
@Default("") String name,
}) = _User;
const User._();
}
ViewModelの実装
import 'dart:async';
import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:card_swiper/user.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final swipeAsyncNotifierProvider =
AsyncNotifierProvider<SwipeAsyncNotifier, List<User>>(
SwipeAsyncNotifier.new);
class SwipeAsyncNotifier extends AsyncNotifier<List<User>> {
@override
// 初期値にUserデータを生成
FutureOr<List<User>> build() {
return List<User>.generate(5, (index) {
return User(
profileImageURL: ["assets/images/flutter.png"],
name: "ジョン${index + 1}",
);
});
}
// スワイプ時の処理
Future<void> swipeOnCard(
AppinioSwiperDirection direction,
) async {
switch (direction) {
case AppinioSwiperDirection.left: // 左方向
// 左方向にスワイプした時の処理
break;
case AppinioSwiperDirection.right: // 右方向
// 右方向にスワイプした時の処理
break;
case AppinioSwiperDirection.top: // 上方向
// 上方向にスワイプした時の処理
break;
case AppinioSwiperDirection.bottom: // 下方向
// 下方向にスワイプした時の処理
break;
default:
}
}
}
初期値にウィジェットに表示するUser
データを生成しています。swipeOnCard
メソッドには、スワイプした方向に応じて何かしらの処理を実行する処理が書かれています。
カードコンポーネントの実装
import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:card_swiper/user.dart';
import 'package:flutter/material.dart';
class SwipeCard extends StatelessWidget {
const SwipeCard({
super.key,
required this.list,
required this.onSwiping,
required this.controller,
});
final List<User> list;
final AppinioSwiperController controller;
final void Function(AppinioSwiperDirection direction) onSwiping;
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: AppinioSwiper(
controller: controller, // スワイプを制御するコントローラー
cardsCount: list.length, // カードの数
onSwiping: onSwiping, // スワイプ中の処理
cardsBuilder: (BuildContext context, int index) {
final user = list[index];
return list.isNotEmpty
? _buildCard(user: user) // カード型ウィジェット
: Center(child: _buildText("No Data"));
},
),
),
_buildActionButton(controller), // アクションボタン
const SizedBox(height: 10),
],
);
}
// カード型コンポーネント
Widget _buildCard({required User user}) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.black, width: 1),
image: DecorationImage(
image: AssetImage(user.profileImageURL[0]),
fit: BoxFit.cover,
alignment: Alignment.center),
),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildText(user.name),
],
),
),
);
}
// アクションボタンコンポーネント
Widget _buildActionButton(AppinioSwiperController controller) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildCustomBtn(
onPressed: () {
controller.swipeLeft(); // 左方向にスワイプ
// 左方向にスワイプした時の処理
},
iconData: Icons.cancel,
color: Colors.red,
),
_buildCustomBtn(
onPressed: () {
controller.swipeUp(); // 上方向にスワイプ
// 上方向にスワイプした時の処理
},
iconData: Icons.star,
color: Colors.blue,
),
_buildCustomBtn(
onPressed: () {
controller.swipeRight(); // 右方向にスワイプ
// 右方向にスワイプした時の処理
},
iconData: Icons.favorite,
color: Colors.teal,
),
],
);
}
// ボタンコンポーネント
Widget _buildCustomBtn({
required void Function()? onPressed,
required IconData iconData,
required Color color,
}) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: Colors.white,
elevation: 8,
shape: const CircleBorder(),
minimumSize: const Size.square(50),
),
child: Icon(
iconData,
color: color,
),
);
}
// テキストコンポーネント
Widget _buildText(String text) {
return Text(
text,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
);
}
}
カードコンポーネント、テキスト、ボタンなどの各種コンポーネントを作成します。AppinioSwiper()
ウィジェットはcardsBuilder
引数にウィジェットを指定することで、上下左右自由にスワイプできるウィジェットを表示することができます。また、AppinioSwiper()
ウィジェットのcontroller
引数にAppinioSwiperController
を指定することで、指定方向のスワイプを制御することが可能となります。
画面の実装
import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:card_swiper/swipe_card.dart';
import 'package:card_swiper/swipe_notifer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerStatefulWidget {
const MainApp({super.key});
@override
ConsumerState<MainApp> createState() => _MainAppState();
}
class _MainAppState extends ConsumerState<MainApp> {
late AppinioSwiperController _swiperController;
@override
void initState() {
super.initState();
// コントローラーの初期化
_swiperController = AppinioSwiperController();
}
@override
void dispose() {
super.dispose();
// コントローラーの破棄
_swiperController.dispose();
}
@override
Widget build(BuildContext context) {
// Userデータの取得
final asyncValue = ref.watch(swipeAsyncNotifierProvider);
// ViewModelの取得
final notifier = ref.read(swipeAsyncNotifierProvider.notifier);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SafeArea(
child: asyncValue.when(
// ローディング中の処理
loading: () => const Center(
child: CircularProgressIndicator(),
),
// データ時の処理
error: (error, _) => const Center(
child: Text("Error"),
),
// データ取得後の処理
data: (data) {
// カードウィジェットの表示
return SwipeCard(
list: data,
controller: _swiperController, // スワイプ制御
onSwiping: (AppinioSwiperDirection direction) async =>
await notifier.swipeOnCard(direction), // スワイプ処理
);
},
),
),
),
);
}
}
AppinioSwiperController
の初期化や破棄、WigetRef
を使用するため、ConsumerStatefulWidget
を使用しています。指でタップすることにより、上下左右にスワイプ可能なことが確認できます。さらに、カードウィジェットの下部にあるボタンにはswiperController
が割り当てられており、左、上、右へのスワイプ処理が実装されています。これによりスワイプの制御が可能となっています。
まとめ
appinio_swiper
パッケージを使用すると、スワイプ(上下左右)可能なウィジェットを簡単に実装することができます。この記事では詳細な実装は行っていませんが、自由にカスタマイズ可能です。あなたのアプリに最適な形に調整してみてください。