# Flutter Model の基本
モデルは、アプリケーションのデータを表現し、データの操作や管理を容易にするために使用されます。
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
// JSONからUserオブジェクトを生成するファクトリコンストラクタ
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] is String ? int.parse(json['id']) : json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
);
}
// UserオブジェクトをJSONに変換するメソッド
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
}
# JSON からモデルへの変換
API などから取得した JSON データをモデルに変換するには、上記のfromJsonファクトリコンストラクタを使用します。
void main() {
// サンプルJSONデータ
Map<String, dynamic> json = {
'id': 123,
'name': 'John Doe',
'email': 'john.doe@example.com',
};
// JSONデータからUserオブジェクトを生成
User user = User.fromJson(json);
print('ID: ${user.id}');
print('Name: ${user.name}');
print('Email: ${user.email}');
}
# モデルから JSON への変換
モデルを JSON 形式で保存したり送信したりするには、toJsonメソッドを使用します。
void main() {
// Userオブジェクトを生成
User user = User(id: 123, name: 'John Doe', email: 'john.doe@example.com');
// UserオブジェクトをJSON形式に変換
Map<String, dynamic> json = user.toJson();
print('JSON: $json');
}
final キーワードについて
- 変数が初期化された後に変更されないことを保証
- コンストラクタ内で初期化する際に使用することをおすすめ
データの不変性を保証することで、予期せぬバグの発生を防ぐことができます。final を積極に使うべきです。
# ディレクトリ構造
lib/
|-- models/
| |-- user.dart
|-- screens/
| |-- home_screen.dart
|-- services/
| |-- api_service.dart
| |-- user_service.dart
|-- widgets/
| |-- user_list.dart
|-- main.dart
lib/models/ディレクトリにモデルクラスを配置することがおすすめです。
user.dartの場合、lib/models/user.dartになりますね。
# UserList 作成してみる
StatelessWidgetを使用して、User モデルのリストを表示する UserList ウィジェットを作成
# API ファイル作成
// lib/services/api_service.dart
import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
class ApiService {
final String baseUrl = dotenv.env['API_BASE_URL'] ?? '';
Future<dynamic> getRequest(String endpoint) async {
final response = await http.get(Uri.parse('$baseUrl$endpoint'));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to load data');
}
}
Future<dynamic> postRequest(String endpoint, Map<String, dynamic> data) async {
final response = await http.post(
Uri.parse('$baseUrl$endpoint'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(data),
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to post data');
}
}
Future<dynamic> putRequest(String endpoint, Map<String, dynamic> data) async {
final response = await http.put(
Uri.parse('$baseUrl$endpoint'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(data),
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to update data');
}
}
Future<dynamic> deleteRequest(String endpoint) async {
final response = await http.delete(Uri.parse('$baseUrl$endpoint'));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to delete data');
}
}
}
// lib/services/user_service.dart
import 'package:my_app/models/user.dart';
import 'api_service.dart';
class UserService {
final ApiService apiService;
UserService({required this.apiService});
Future<List<User>> fetchUsers() async {
final data = await apiService.getRequest('/users');
return (data as List).map((json) => User.fromJson(json)).toList();
}
Future<User> updateUser(String id, Map<String, dynamic> data) async {
final response = await apiService.putRequest('/users/$id', data);
return User.fromJson(response);
}
Future<void> deleteUser(String id) async {
await apiService.deleteRequest('/users/$id');
}
}
# UserList ウィジェットの作成
// lib/widgets/user_list.dart
import 'package:flutter/material.dart';
import 'package:my_app/models/user.dart';
import 'package:my_app/services/user_service.dart';
class UserList extends StatelessWidget {
final List<User> users;
final UserService userService;
const UserList({super.key, required this.users, required this.userService});
Widget build(BuildContext context) {
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].name),
subtitle: Text(users[index].email),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
await userService.deleteUser(users[index].id);
},
),
],
),
);
},
);
}
}
# UserList ウィジェットの使用してホームスクリーンを作成
// lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import 'package:my_app/models/user.dart';
import 'package:my_app/services/api_service.dart';
import 'package:my_app/services/user_service.dart';
import 'package:my_app/widgets/user_list.dart';
class HomeScreen extends StatefulWidget {
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
late Future<List<User>> futureUsers;
late UserService userService;
void initState() {
super.initState();
userService = UserService(apiService: ApiService());
futureUsers = userService.fetchUsers();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('User List'),
),
body: FutureBuilder<List<User>>(
future: futureUsers,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Failed to load users'));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('No users found'));
} else {
return UserList(users: snapshot.data!, userService: userService);
}
},
),
);
}
}
# main dart 修正
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:my_app/screens/home_screen.dart';
void main() async {
await dotenv.load(fileName: ".env");
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: HomeScreen(),
);
}
}