Dartでのロリポップサーバーとの接続1
公開日: 2024-05-13 00:36:05
今回はFlutterからロリポップサーバーのデータを取得するコードです。
Swiftで作成したコードがあったのでそちらをベースに作成しています。
selectでGet通信で書いていたので、今回はGet通信のサンプルコードです。
model.dart
class DataItem {
final String id;
final String date;
final String userId;
final String quest;
final String? mainText;
final String? urlText;
final String item1;
final String item2;
DataItem({
required this.id,
required this.date,
required this.userId,
required this.quest,
this.mainText,
this.urlText,
required this.item1,
required this.item2,
});
factory DataItem.fromJson(Map<String, dynamic> json) {
return DataItem(
id: json['ID'],
date: json['DATE'],
userId: json['USERID'],
quest: json['QUEST'],
mainText: json['MAINTEXT'],
urlText: json['URLTEXT'],
item1: json['ITEM1'],
item2: json['ITEM2'],
);
}
}
view.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../viewmodel/viewmodel.dart';
void main() {
runApp(ChangeNotifierProvider(
// ViewModelのインスタンスをプロバイダーで生成
create: (context) => DataViewModel(),
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('MVVM API Demo'),
),
body: Center(
child: Consumer<DataViewModel>(
builder: (context, viewModel, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => viewModel
// ボタンが押された時にViewModelのfetchDataを呼び出す
.fetchData(),
child: const Text('データを取得'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child:
// ViewModelからデータを取得して表示
Text(viewModel.data as String),
),
],
);
},
),
),
),
);
}
}
viewmodel.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../model/model.dart';
// ViewModelクラスを定義します。このクラスはChangeNotifierを継承しており、
// UIの状態更新をリスナーに通知することができます。
class DataViewModel extends ChangeNotifier {
// データリストを保持するリスト
List<DataItem> dataList = [];
int currentPage = 0;
int currentOffset = 0;
// データ取得時に一度に読み込む数
final int loadIncrement = 20;
// データ読み込み中かどうかの状態
bool isRefreshing = false;
// APIからデータを非同期に取得し、状態を更新します。
Future<void> fetchData({bool initial = false}) async {
// 初期化処理
if (initial) {
currentPage = 0;
currentOffset = 0;
dataList = [];
} else {
// 初期化でない場合、ページとオフセットを更新
currentPage += 1;
currentOffset += loadIncrement;
}
// APIのURLを作成
String baseString = "https://www.test1234server/select.php";
String urlString = "$baseString?offset=$currentOffset&limit=$loadIncrement";
print(urlString);
try {
// HTTP GETリクエストを送信します。
var url = Uri.parse(urlString);
var response = await http.get(url);
// レスポンスが成功時
if (response.statusCode == 200) {
var decodedData = jsonDecode(response.body) as List;
var items = decodedData.map((itemJson) => DataItem.fromJson(itemJson)).toList();
if (initial) {
// 初期ロードの場合はリストを新データで置き換え
dataList = items;
} else {
// 追加ロードの場合はリストにデータを追加
dataList.addAll(items);
}
// UIのリスナーに状態変更を通知
notifyListeners();
} else {
// レスポンスが失敗の場合
throw Exception('Failed to load data');
}
} catch (e) {
// ネットワークエラーやその他の例外の場合
print("Error loading data: $e");
}
}
// データリストのゲッターを提供
// UI側からデータリストへのアクセスを容易にする
List<DataItem> get data => dataList;
}
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../viewmodel/viewmodel.dart';
void main() {
runApp(ChangeNotifierProvider(
// DataViewModelをアプリ全体で利用可能にするためにProviderを設定
create: (context) => DataViewModel(), // ViewModelのインスタンスをプロバイダーで生成
child: const MyApp(), // MyAppウィジェットをルートウィジェットとして使用
));
}
// アプリケーションのルートウィジェット定義
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// MaterialAppウィジェットを使用して、ナビゲーションやテーマなどの基本的なデザイン環境を提供
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('MVVM API Demo'), // アプリバーにタイトルを設定
),
body: Center(
// DataViewModelを使用するためにConsumerウィジェットを配置
child: Consumer<DataViewModel>(
// Consumerを使用してDataViewModelのデータをリアクティブに表示
builder: (context, viewModel, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => viewModel.fetchData(), // ボタン押下でfetchDataを実行
child: const Text('データを取得'), // ボタンのラベル
),
Padding(
padding: const EdgeInsets.all(8.0),
// 条件によって表示するテキストを変更
child: viewModel.dataList.isNotEmpty
// データがある場合は最初の要素を表示
? Text('ID: ${viewModel.dataList.first.id}, USER: ${viewModel.dataList.first.userId}')
: const Text('データがありません'), // データがない場合の表示
),
],
);
},
),
),
),
);
}
}
server側
select.php
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
$servername = "mysql999.phy.lolipop.lan";
$username = "LAA9999999";
$password = "test1234test1234";
$dbname = "test1234";
// MySQLへの接続を確立
$conn = new mysqli($servername, $username, $password, $dbname);
// 接続の確認
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// オフセットとリミットを取得
$offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10; // 既定値は10
// SQLクエリを作成(Testテーブルから指定された範囲のデータを取得)
$sql = "SELECT * FROM Test
ORDER BY DATE DESC
LIMIT $limit OFFSET $offset";
$result = $conn->query($sql);
// データの取得と出力
if ($result->num_rows > 0) {
$data = array();
while($row = $result->fetch_assoc()) {
$data[] = $row;
}
// JSON形式で出力
header('Content-Type: application/json');
echo json_encode($data);
} else {
echo "No records found!";
}
// 接続を閉じる
$conn->close();
?>
実行結果

SwiftではGet通信から簡単に取得できたのですが、FlutterではWebリソースへのアクセスの取り扱いが違うようで、Swiftより厳しいようで、サーバー側のファイルselect.phpファイルのheader()から始まる頭の3行を追加しないと、responsの取得部分で失敗し続けます。
今回はSelectでデータを読み込む際のGet通信の書き方のサンプルコードですが、書き込み処理はPost通信で行う必要があるので、そちらのコードを載せます。