for, for in, while, map 繰り返し処理
対象者
- Dartでループ処理を書いてる人
- 普段は、mapしか書けなくて困ってる人💦
- forで書くにはどうするのか?
Dartのロジックを書くときに、 map
ばかり使っております。しかしリファレンスのコードを読むと、for
, for in
, while
が出てくる😅
map
しか書けないどうしょう〜😭
筆者はロジックに悩むことがあります。最近はアルゴリズムの問題を解いたりして、map
以外で書けないのかと探求してます。
APIからデータを取ってくるこのコードを書き換えて使っていこうと思います。おそらく中級者向けの記事かなと思うので、初心者はやめておいた方がいいかも(−_−;)
riverpod + freezedが必要なので追加しておいてください。
APIはこちら使います。
freezedでモデルクラスを作成。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'posts.freezed.dart';
part 'posts.g.dart';
class Posts with _$Posts {
const factory Posts({
required int id,
required String title,
}) = _Posts;
factory Posts.fromJson(Map<String, Object?> json)
=> _$PostsFromJson(json);
}
map は、Listの要素を 1 つずつ取り出して、処理を実行する。forEach との違いは、map は、Listの要素を 1 つずつ取り出して、処理を実行するだけでなく、処理の結果をListにして返す。
void main() {
var names = ['山田', '佐藤', '鈴木'];
var upperNames = names.map((name) {
return name;
});
print(upperNames);// (山田, 佐藤, 鈴木)
}
http
を使用して、 APIと通信するロジックを作ります。こちらのコードを後で書き換えていきます。Listに取得したデータを格納して、Mapになってるのでこちらを.toList
で、Listに変換してあげましょう。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:provider_lesson/model/posts.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.g.dart';
(keepAlive: true)
Client client(ClientRef ref) {
return Client(http.Client());
}
class Client {
final http.Client client;
Client(this.client);
Future<List<Posts>> fetchPost() async {
try {
List<Posts> posts = [];
const url = 'https://jsonplaceholder.typicode.com/posts';
final response = await client.get(Uri.parse(url));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
// map
posts = data.map((json) => Posts.fromJson(json)).toList();
return posts;
} else {
return [];
}
} catch (e) {
throw Exception('Failed to load post');
}
}
}
View側に表示するには、AsyncNotifierを使います。build
メソッドの中に、初期化された時の処理を書いて、ページが呼ばれたらデータを表示できるようにしましょう。
import 'package:provider_lesson/api/api.dart';
import 'package:provider_lesson/model/posts.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'async_post.g.dart';
class AsyncPost extends _$AsyncPost {
FutureOr<List<Posts>> build() {
return fetchPost();
}
Future<List<Posts>> fetchPost() async {
final fetch = await ref.read(clientProvider).fetchPost();
return fetch;
}
}
View側のコードは今回は、main.dart
に全て書きました。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:provider_lesson/provider/async_post.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyWidget(),
);
}
}
class MyWidget extends ConsumerWidget {
const MyWidget({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final post = ref.watch(asyncPostProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo While'),
),
body: switch(post) {
AsyncData(:final value) => ListView.builder(
itemCount: value.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(value[index].title),
);
},
),
AsyncError(:final error) => Center(child: Text('Error: $error')),
_=> const Center(child: CircularProgressIndicator()),
}
);
}
}
これを書き換えていきます。
プロジェクトの説明
繰り返し処理の基本のfor
を使ってみよう。他の言語でもよくある書き方の回数を指定しただけループ処理をしてみる。
for文
void main() {
// 0から9までの数字を出力する
for (var i = 0; i < 10; i++) {
print(i);// 0から9まで出力
}
}
見本は、10回だけどリストの値を数えて、空っぽのリストに、.add
で格納してあげます。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:provider_lesson/model/posts.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.g.dart';
(keepAlive: true)
Client client(ClientRef ref) {
return Client(http.Client());
}
class Client {
final http.Client client;
Client(this.client);
Future<List<Posts>> fetchPost() async {
try {
List<Posts> posts = [];
const url = 'https://jsonplaceholder.typicode.com/posts';
final response = await client.get(Uri.parse(url));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
for(var i = 0; i < data.length; i++) {
posts.add(Posts.fromJson(data[i]));
}
return posts;
} else {
return [];
}
} catch (e) {
throw Exception('Failed to load post');
}
}
}
for in
for文には、 for in がある。
for in は、Listの要素を 1 つずつ取り出して、処理を実行する。
void main() {
var names = ['山田', '佐藤', '鈴木'];
for (var name in names) {
print(name);// 山田 佐藤 鈴木
}
}
APIのデータを扱うときは、i in data
と書いて、.add
でリストにデータを格納できます。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:provider_lesson/model/posts.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.g.dart';
(keepAlive: true)
Client client(ClientRef ref) {
return Client(http.Client());
}
class Client {
final http.Client client;
Client(this.client);
Future<List<Posts>> fetchPost() async {
try {
List<Posts> posts = [];
const url = 'https://jsonplaceholder.typicode.com/posts';
final response = await client.get(Uri.parse(url));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
// for in
for(var i in data) {
posts.add(Posts.fromJson(i));
}
return posts;
} else {
return [];
}
} catch (e) {
throw Exception('Failed to load post');
}
}
}
while文
これは珍しい。ゲーム開発ではよく使う文法だそうですが、普段 Flutterでは使うことはないですね。Widgetでつかうとパフォーマンスを落とすらしい。ロジックでは問題ない。
while 文は条件式が true の間、繰り返し処理を実行する。
void main() {
var i = 0;// 0からスタート
while (i < 10) {// 10未満の間、繰り返し処理を実行
print(i);// 0から9まで出力
i++;// 1ずつ増やす
}
}
APIで使った例。条件が、isNotEmptyでなければデータを追加する。removeAt
は指定したindex
の要素を削除するメソッドです。
while
文書くときは、条件分岐のa == null
とかをfor
文だったら{}
の中に書くけど、最初からやってるイメージです。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:provider_lesson/model/posts.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.g.dart';
(keepAlive: true)
Client client(ClientRef ref) {
return Client(http.Client());
}
class Client {
final http.Client client;
Client(this.client);
Future<List<Posts>> fetchPost() async {
try {
List<Posts> posts = [];
const url = 'https://jsonplaceholder.typicode.com/posts';
final response = await client.get(Uri.parse(url));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
// while
while (data.isNotEmpty) {
posts.add(Posts.fromJson(data.removeAt(0)));
}
return posts;
} else {
return [];
}
} catch (e) {
throw Exception('Failed to load post');
}
}
}
感想
今回は、map
以外で繰り返し処理をやってみました。公式のリファレンスにチュートリアルがあるので、やってみると参考になるかもしれません。普段無意識で使ってると書いてるけど、for最近まで使ってなかった笑
ロジックに悩まない男になりたい😅
Discussion