Flutter ウィジェット早見表:レイアウト・入力・ナビゲーションを 1 枚で把握
Flutter アプリケーション開発では、数多くのウィジェットを組み合わせて UI を構築していきます。しかし、初心者の方にとっては「どのウィジェットをどんな場面で使えばいいのか」を把握するのが難しいと感じることもあるでしょう。本記事では、レイアウト・入力・ナビゲーションに特化した主要ウィジェットを、1 枚の早見表にまとめてご紹介します。
それぞれのウィジェットの役割や使用例を理解することで、開発効率が大幅に向上し、より直感的に UI を設計できるようになるでしょう。
レイアウトウィジェット早見表
以下の表は、Flutter でよく使用されるレイアウトウィジェットをまとめたものです。
| # | ウィジェット名 | 主な用途 | 配置方向 | 特徴 |
|---|---|---|---|---|
| 1 | Container | 単一の子要素を装飾・配置 | - | パディング、マージン、背景色、境界線などを設定可能 |
| 2 | Column | 複数の子要素を縦方向に配置 | 縦 | 垂直方向のリスト表示に最適 |
| 3 | Row | 複数の子要素を横方向に配置 | 横 | 水平方向のリスト表示に最適 |
| 4 | Stack | 子要素を重ねて配置 | 重ね合わせ | 画像の上にテキストを配置するなどに使用 |
| 5 | Expanded | 親要素の余白を埋めるように拡張 | - | Column/Row 内で比率に基づいた配置が可能 |
| 6 | Flexible | 柔軟にサイズ調整可能な配置 | - | 子要素のサイズを柔軟に変更 |
| 7 | Padding | 子要素に余白を追加 | - | 内側の余白を設定 |
| 8 | Center | 子要素を中央に配置 | - | 縦横中央揃えに使用 |
| 9 | Align | 子要素を任意の位置に配置 | - | 上下左右の任意の位置に配置可能 |
| 10 | SizedBox | 固定サイズのボックス | - | 余白の作成やサイズ制限に使用 |
| 11 | Wrap | 子要素を折り返して配置 | 自動折り返し | タグやチップの表示に最適 |
| 12 | ListView | スクロール可能なリスト | 縦/横 | 動的なリスト表示に使用 |
| 13 | GridView | グリッド状に配置 | グリッド | 画像ギャラリーなどに最適 |
入力ウィジェット早見表
ユーザーからの入力を受け付けるウィジェットの一覧です。
| # | ウィジェット名 | 主な用途 | 入力タイプ | 特徴 |
|---|---|---|---|---|
| 1 | TextField | テキスト入力 | テキスト | 単一行・複数行のテキスト入力に対応 |
| 2 | TextFormField | フォームでのテキスト入力 | テキスト | バリデーション機能を持つテキスト入力 |
| 3 | Checkbox | チェックボックス | 真偽値 | オン/オフの二択選択 |
| 4 | Radio | ラジオボタン | 選択肢 | 複数の選択肢から一つを選択 |
| 5 | Switch | スイッチ | 真偽値 | トグル式のオン/オフ切り替え |
| 6 | Slider | スライダー | 数値 | 範囲内の数値を選択 |
| 7 | DropdownButton | ドロップダウンメニュー | 選択肢 | リストから一つを選択 |
| 8 | DatePicker | 日付選択 | 日付 | カレンダーから日付を選択 |
| 9 | TimePicker | 時刻選択 | 時刻 | 時刻を選択 |
| 10 | Form | フォーム全体の管理 | - | 複数の入力フィールドをグループ化 |
ナビゲーションウィジェット早見表
画面遷移やナビゲーションに関連するウィジェットの一覧です。
| # | ウィジェット名 | 主な用途 | 遷移タイプ | 特徴 |
|---|---|---|---|---|
| 1 | Navigator | 画面遷移の管理 | プッシュ/ポップ | スタック構造で画面を管理 |
| 2 | MaterialPageRoute | Material デザインの画面遷移 | プッシュ | アニメーション付き画面遷移 |
| 3 | BottomNavigationBar | 下部ナビゲーションバー | タブ切り替え | 3〜5 個のタブで画面切り替え |
| 4 | TabBar | タブバー | タブ切り替え | 上部タブでコンテンツ切り替え |
| 5 | Drawer | サイドメニュー | スライドメニュー | 左右からスライドするメニュー |
| 6 | AppBar | アプリバー | - | 画面上部のヘッダー |
| 7 | PageView | スワイプで画面切り替え | スワイプ | 横スワイプでページ遷移 |
| 8 | CupertinoNavigationBar | iOS スタイルのナビゲーションバー | - | iOS 風のヘッダー |
| 9 | CupertinoTabBar | iOS スタイルのタブバー | タブ切り替え | iOS 風の下部タブバー |
背景
Flutter は Google が開発したクロスプラットフォーム開発フレームワークで、単一のコードベースから iOS、Android、Web、デスクトップアプリケーションを構築できます。
Flutter の特徴は、すべてがウィジェットで構成されていることです。画面に表示されるあらゆる要素、レイアウト、装飾、アニメーションに至るまで、すべてがウィジェットとして実装されています。
mermaidflowchart TB
app["Flutter アプリ"] --> ui["UI レイヤー"]
ui --> layout["レイアウト<br/>ウィジェット"]
ui --> input["入力<br/>ウィジェット"]
ui --> navi["ナビゲーション<br/>ウィジェット"]
layout --> column["Column"]
layout --> row["Row"]
layout --> stack["Stack"]
input --> textfield["TextField"]
input --> checkbox["Checkbox"]
input --> slider["Slider"]
navi --> navigator["Navigator"]
navi --> bottomnav["BottomNavigationBar"]
navi --> drawer["Drawer"]
上記の図は、Flutter アプリの UI がどのようにウィジェットで構成されているかを示しています。レイアウト、入力、ナビゲーションの 3 つのカテゴリに分類され、それぞれが特定の役割を担っています。
Flutter におけるウィジェットの役割
Flutter では、ウィジェットツリーと呼ばれる階層構造で UI を構築します。親ウィジェットの中に子ウィジェットを配置し、それを繰り返すことで複雑な UI を実現します。
この仕組みにより、以下のようなメリットが得られます。
- 再利用性の高さ: 一度作成したウィジェットを複数の場所で再利用できます
- 保守性の向上: コンポーネント単位で管理できるため、変更が容易です
- カスタマイズの柔軟性: 既存のウィジェットを組み合わせて、独自のウィジェットを作成できます
しかし、Flutter には数百種類ものウィジェットが用意されているため、初学者にとっては「どのウィジェットを選べば良いか」が大きな課題となるでしょう。
課題
Flutter 開発において、多くの開発者が直面する課題は以下の通りです。
ウィジェット選択の難しさ
Flutter には膨大な数のウィジェットが存在し、似たような機能を持つウィジェットも多数あります。例えば、テキスト入力には TextField と TextFormField があり、それぞれ異なる用途に最適化されています。
初心者の方は、これらの違いを理解せずに使用してしまい、後から問題が発生することがあります。
レイアウト構築の複雑さ
レイアウトを構築する際、Column、Row、Stack、Expanded、Flexible などのウィジェットを適切に組み合わせる必要があります。これらの使い分けができないと、期待通りのレイアウトを実現できません。
特に、Expanded と Flexible の違いや、mainAxisAlignment と crossAxisAlignment の使い分けは、初学者にとって理解が難しい部分です。
ナビゲーション実装の迷い
画面遷移やナビゲーションの実装では、Navigator、BottomNavigationBar、Drawer、TabBar など、複数の選択肢があります。アプリの要件に応じて適切なナビゲーション方法を選択する必要がありますが、どれを選べば良いか迷うことが多いでしょう。
mermaidflowchart LR
dev["開発者"] -->|悩み| choice{"ウィジェット<br/>選択"}
choice -->|多すぎる| problem1["選択肢が<br/>多すぎる"]
choice -->|違いが不明| problem2["類似ウィジェット<br/>の違いが不明"]
choice -->|使い分け| problem3["レイアウト<br/>組み合わせが複雑"]
problem1 --> result["開発効率<br/>低下"]
problem2 --> result
problem3 --> result
この図は、開発者がウィジェット選択で直面する主な課題を示しています。選択肢の多さ、類似ウィジェットの違いの不明瞭さ、レイアウト組み合わせの複雑さが、開発効率の低下につながります。
解決策
上記の課題を解決するためには、以下のアプローチが効果的です。
カテゴリ別にウィジェットを整理する
ウィジェットをレイアウト、入力、ナビゲーションの 3 つのカテゴリに分類することで、目的に応じて必要なウィジェットを素早く見つけることができます。
本記事の冒頭で示した早見表は、この分類に基づいて作成されています。開発時にこの表を参照することで、適切なウィジェットを迅速に選択できるでしょう。
主要ウィジェットの特徴を理解する
各ウィジェットの主な用途と特徴を把握することが重要です。例えば、以下のような理解が必要です。
Columnは縦方向に子要素を配置するRowは横方向に子要素を配置するStackは要素を重ねて配置するTextFieldは単純なテキスト入力に使用するTextFormFieldはバリデーション機能が必要な場合に使用する
サンプルコードで実践的に学ぶ
理論だけでなく、実際にコードを書いて動作を確認することが最も効果的な学習方法です。次のセクションでは、各カテゴリの代表的なウィジェットを使用した具体例をご紹介します。
mermaidflowchart TB
start["ウィジェット<br/>選択開始"] --> category{"カテゴリ<br/>選択"}
category -->|画面構成| layout["レイアウト<br/>ウィジェット"]
category -->|ユーザー入力| input["入力<br/>ウィジェット"]
category -->|画面遷移| navi["ナビゲーション<br/>ウィジェット"]
layout --> check1{"目的に<br/>合致?"}
input --> check2{"目的に<br/>合致?"}
navi --> check3{"目的に<br/>合致?"}
check1 -->|Yes| implement["実装"]
check2 -->|Yes| implement
check3 -->|Yes| implement
check1 -->|No| category
check2 -->|No| category
check3 -->|No| category
implement --> done["完成"]
この図は、ウィジェット選択のフローを示しています。まずカテゴリを選択し、目的に合致するウィジェットを見つけるまで探索を続けます。
図で理解できる要点:
- ウィジェット選択は 3 つのカテゴリから開始する
- 目的に合致しない場合は再度カテゴリ選択に戻る
- 適切なウィジェットが見つかれば実装フェーズへ進む
具体例
それでは、各カテゴリの代表的なウィジェットを使用した具体的なコード例を見ていきましょう。
レイアウトウィジェットの実装例
Column と Row の基本的な使い方
Column と Row は最も基本的なレイアウトウィジェットです。以下のコードは、縦方向と横方向に要素を配置する例を示しています。
dartimport 'package:flutter/material.dart';
まず、Flutter の基本パッケージをインポートします。
dartclass LayoutExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('レイアウト例'),
),
body: Column(
children: [
// 縦方向に要素を配置
Container(
color: Colors.blue,
height: 100,
child: Center(child: Text('ヘッダー')),
),
// 横方向に要素を配置
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
color: Colors.red,
width: 100,
height: 100,
child: Center(child: Text('左')),
),
Container(
color: Colors.green,
width: 100,
height: 100,
child: Center(child: Text('中央')),
),
Container(
color: Colors.orange,
width: 100,
height: 100,
child: Center(child: Text('右')),
),
],
),
],
),
);
}
}
この例では、Column で縦方向にヘッダーとコンテンツエリアを配置し、コンテンツエリア内で Row を使って 3 つのボックスを横並びにしています。
mainAxisAlignment プロパティに spaceEvenly を指定することで、各要素が均等に配置されます。
Stack を使った重ね合わせレイアウト
Stack を使用すると、要素を重ねて配置できます。画像の上にテキストを配置する場合などに便利です。
dartclass StackExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: [
// 背景画像
Container(
width: 300,
height: 200,
color: Colors.grey,
),
// 重ねて表示するテキスト
Positioned(
bottom: 20,
right: 20,
child: Container(
padding: EdgeInsets.all(8),
color: Colors.black54,
child: Text(
'画像の説明',
style: TextStyle(color: Colors.white),
),
),
),
],
);
}
}
Positioned ウィジェットを使用することで、Stack 内の要素の配置位置を細かく制御できます。この例では、右下にテキストを配置しています。
Expanded を使った柔軟なレイアウト
Expanded を使用すると、利用可能なスペースを自動的に埋めることができます。
dartclass ExpandedExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
// 固定幅のコンテナ
Container(
width: 100,
color: Colors.red,
child: Center(child: Text('固定')),
),
// 残りのスペースを埋めるコンテナ
Expanded(
child: Container(
color: Colors.blue,
child: Center(child: Text('拡張')),
),
),
// 固定幅のコンテナ
Container(
width: 100,
color: Colors.green,
child: Center(child: Text('固定')),
),
],
);
}
}
この例では、左右に固定幅のコンテナを配置し、中央の Expanded ウィジェットが残りのスペースを自動的に埋めます。画面サイズが変わっても、レイアウトが適切に調整されるでしょう。
入力ウィジェットの実装例
TextField によるテキスト入力
TextField は最も基本的なテキスト入力ウィジェットです。
dartclass TextFieldExample extends StatefulWidget {
@override
_TextFieldExampleState createState() => _TextFieldExampleState();
}
class _TextFieldExampleState extends State<TextFieldExample> {
// テキスト入力の内容を管理するコントローラー
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
// コントローラーの破棄
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'ユーザー名',
hintText: '名前を入力してください',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 入力内容を取得
print('入力された値: ${_controller.text}');
},
child: Text('送信'),
),
],
);
}
}
TextEditingController を使用することで、入力内容の取得や変更が簡単に行えます。decoration プロパティでラベルやヒントテキスト、ボーダーなどの装飾を設定できます。
Form と TextFormField によるバリデーション
フォーム全体を管理し、バリデーションを行う場合は Form と TextFormField を組み合わせます。
dartclass FormExample extends StatefulWidget {
@override
_FormExampleState createState() => _FormExampleState();
}
class _FormExampleState extends State<FormExample> {
// フォームの状態を管理するキー
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'メールアドレス'),
// バリデーション処理
validator: (value) {
if (value == null || value.isEmpty) {
return 'メールアドレスを入力してください';
}
if (!value.contains('@')) {
return '有効なメールアドレスを入力してください';
}
return null;
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// フォームのバリデーションを実行
if (_formKey.currentState!.validate()) {
print('バリデーション成功');
}
},
child: Text('送信'),
),
],
),
);
}
}
validator プロパティで入力内容の検証ロジックを定義します。_formKey.currentState!.validate() を呼び出すことで、すべてのフィールドのバリデーションを一度に実行できます。
Checkbox、Radio、Switch の使用例
選択式の入力ウィジェットの使用例を見てみましょう。
dartclass SelectionExample extends StatefulWidget {
@override
_SelectionExampleState createState() => _SelectionExampleState();
}
class _SelectionExampleState extends State<SelectionExample> {
// チェックボックスの状態
bool _checkboxValue = false;
// ラジオボタンの選択値
int _radioValue = 1;
// スイッチの状態
bool _switchValue = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
// チェックボックス
CheckboxListTile(
title: Text('利用規約に同意する'),
value: _checkboxValue,
onChanged: (bool? value) {
setState(() {
_checkboxValue = value ?? false;
});
},
),
// ラジオボタン
RadioListTile<int>(
title: Text('オプション 1'),
value: 1,
groupValue: _radioValue,
onChanged: (int? value) {
setState(() {
_radioValue = value ?? 1;
});
},
),
RadioListTile<int>(
title: Text('オプション 2'),
value: 2,
groupValue: _radioValue,
onChanged: (int? value) {
setState(() {
_radioValue = value ?? 2;
});
},
),
// スイッチ
SwitchListTile(
title: Text('通知を有効にする'),
value: _switchValue,
onChanged: (bool value) {
setState(() {
_switchValue = value;
});
},
),
],
);
}
}
CheckboxListTile、RadioListTile、SwitchListTile を使用することで、タイトル付きの選択式入力が簡単に実装できます。setState を呼び出すことで、UI が自動的に更新されます。
ナビゲーションウィジェットの実装例
Navigator による画面遷移
Navigator を使用した基本的な画面遷移の例です。
dart// 最初の画面
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('最初の画面')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 次の画面へ遷移
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Text('次の画面へ'),
),
),
);
}
}
Navigator.push メソッドで新しい画面をスタックにプッシュします。MaterialPageRoute は Material Design のアニメーション付き画面遷移を提供します。
dart// 2番目の画面
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('2番目の画面')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 前の画面へ戻る
Navigator.pop(context);
},
child: Text('戻る'),
),
),
);
}
}
Navigator.pop メソッドで現在の画面をスタックから取り除き、前の画面に戻ります。AppBar の戻るボタンも自動的に表示されます。
BottomNavigationBar の実装
下部ナビゲーションバーを使用したタブ切り替えの例です。
dartclass BottomNavExample extends StatefulWidget {
@override
_BottomNavExampleState createState() => _BottomNavExampleState();
}
class _BottomNavExampleState extends State<BottomNavExample> {
// 現在選択されているタブのインデックス
int _selectedIndex = 0;
// 各タブで表示する画面のリスト
final List<Widget> _pages = [
Center(child: Text('ホーム画面')),
Center(child: Text('検索画面')),
Center(child: Text('設定画面')),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('タブナビゲーション')),
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'ホーム',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: '検索',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '設定',
),
],
),
);
}
}
BottomNavigationBar の currentIndex プロパティで現在のタブを管理し、onTap コールバックでタブの切り替えを処理します。タブに応じて表示する画面を切り替えることができます。
Drawer によるサイドメニュー
サイドメニューを実装する例を見てみましょう。
dartclass DrawerExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Drawer 例'),
),
// サイドメニューの実装
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
// ヘッダー部分
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'メニュー',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
// メニュー項目
ListTile(
leading: Icon(Icons.home),
title: Text('ホーム'),
onTap: () {
Navigator.pop(context);
// ホーム画面への遷移処理
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('設定'),
onTap: () {
Navigator.pop(context);
// 設定画面への遷移処理
},
),
ListTile(
leading: Icon(Icons.info),
title: Text('情報'),
onTap: () {
Navigator.pop(context);
// 情報画面への遷移処理
},
),
],
),
),
body: Center(
child: Text('メイン画面'),
),
);
}
}
Scaffold の drawer プロパティに Drawer ウィジェットを設定することで、左からスライドするメニューが実装できます。AppBar のハンバーガーメニューアイコンも自動的に表示されるでしょう。
mermaidflowchart LR
user["ユーザー"] -->|操作| nav["ナビゲーション<br/>選択"]
nav -->|画面遷移| push["Navigator.push"]
nav -->|タブ切り替え| bottom["BottomNavigationBar"]
nav -->|メニュー表示| drawer["Drawer"]
push --> screen["新しい画面"]
bottom --> tab["タブ画面"]
drawer --> menu["メニュー項目"]
screen -->|Navigator.pop| nav
tab -->|タップ| nav
menu -->|タップ| nav
この図は、ナビゲーションの基本的なフローを示しています。ユーザーの操作に応じて、画面遷移、タブ切り替え、メニュー表示のいずれかが実行され、再びナビゲーション選択に戻ります。
図で理解できる要点:
- ナビゲーションは大きく 3 つのパターンに分類される
- 各ナビゲーション方法は独自の UI パターンを持つ
- すべてのナビゲーションは循環的なフローで機能する
まとめ
本記事では、Flutter の主要ウィジェットをレイアウト、入力、ナビゲーションの 3 つのカテゴリに分けてご紹介しました。
早見表を活用することで、開発時に必要なウィジェットを素早く見つけることができるでしょう。また、各ウィジェットの具体的な使用例を通して、実装方法も理解できたのではないでしょうか。
Flutter 開発では、これらのウィジェットを組み合わせることで、複雑な UI を構築していきます。最初はシンプルなレイアウトから始めて、徐々に複雑な構成に挑戦していくことをおすすめします。
ウィジェットの選択に迷ったときは、本記事の早見表に戻ってきて、目的に応じた最適なウィジェットを見つけてください。継続的に学習を進めることで、より効率的で美しい UI を実装できるようになるでしょう。
関連リンク
articleFlutter ウィジェット早見表:レイアウト・入力・ナビゲーションを 1 枚で把握
articleFlutter 開発環境の最短構築:macOS/Windows/Linux 別インストール完全ガイド
articleFlutter とは?2025 年版:仕組み・強み・向いているプロダクトを徹底解説
articleGrok プロンプト・テンプレ 100 連発:要約/翻訳/コード/分析 早見表
articleGitHub Copilot Workspace と Cursor/Cline の比較検証:仕様駆動の自動化能力はどこまで?
articleGitHub Actions 署名戦略を比べる:SHA ピン留め vs タグ参照 vs バージョン範囲
articlegpt-oss が OOM/VRAM 枯渇で落ちる:モデル分割・ページング・バッチ制御の解決策
articleGPT-5 ツール呼び出しが暴走する時の診断フロー:関数設計/停止条件/リトライ制御
articleGit の index.lock 残留問題を解決:並行実行・クラッシュ後の正しい対処法
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来