【Flutter】Cannot set the body fields of a Request with content-type… エラーの解決方法
Containerにmarginを設定する時、 EdgeInsets.only() で全て設定していませんか?
この記事では、Containerにmargin(外側の余白)/padding(内側の余白)を設定するいろいろな方法についてご紹介します。言い換えると、その設定に使用する EdgeInsets についてざっくりまとめていきます。
$ flutter doctor
[✓] Flutter (Channel stable, 1.22.1, on Mac OS X 10.15.7 19H2, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 12.1)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.50.1)
まずはmargin/paddingを設定する方法から説明します。(分かる人は飛ばしてください)
かなり基本になりますが、Containerを表示する際にmarginを設定すると、要素の外側に一定の余白をとることができます。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('PlayGround'),
),
body: Container(
margin: EdgeInsets.only(left: 100),
color: Colors.redAccent,
width: 100,
height: 100,
),
),
);
}
}
margin: EdgeInsets.only(left: 100) とすることで、赤色の図形の左側に余白ができました。
同じ要領でpaddingを設定すると、図形の内部にも余白を指定することが可能です。
return Container(
margin: EdgeInsets.only(top: 100),
padding: EdgeInsets.only(top: 50),
color: Colors.redAccent,
width: 200,
height: 100,
child: Text('test'),
);
この例ではpaddingでContainerの内部に top:50 と設定しているため、Container内部のTextが下にずれて表示されています。
このような形で要素間に一定のスペースをとるためにmargin/paddingは頻繁に使用されます。
参考程度に、上記のスクリーンショットは僕自身が開発しているアプリの1画面ですが、この画面を構成している各要素もmargin/paddingが各所に設定されています。
次以降の章では、このmargin/paddingを設定する際に使用しているEdgeInsetsの様々な設定方法について説明していきます。
EdgeInset.only()をひたすらに使っていっても問題はありませんが、EdgeInsetsにはもっと便利なメソッドがあります。
今回はその中でよく見かけるメソッドをピックアップしてご紹介します。
/// Creates insets with only the given values non-zero.
///
/// {@tool snippet}
///
/// Left margin indent of 40 pixels:
///
/// ```dart
/// const EdgeInsets.only(left: 40.0)
/// ```
/// {@end-tool}
const EdgeInsets.only({
this.left = 0.0,
this.top = 0.0,
this.right = 0.0,
this.bottom = 0.0,
});
EdgeInsets.only() は引数を4つとることができ、上下左右の各方向へ自由に余白を設定することができます。
return Container(
,margin: EdgeInsets.only(top: 50, left: 100),,
color: Colors.green,
width: 100,
height: 100,
);
EdgeInsets.only() を使うことで、left: 100, top:50 と各方向へ別々の値を設定することができます。
/// Creates insets where all the offsets are `value`.
///
/// {@tool snippet}
///
/// Typical eight-pixel margin on all sides:
///
/// ```dart
/// const EdgeInsets.all(8.0)
/// ```
/// {@end-tool}
const EdgeInsets.all(double value)
: left = value,
top = value,
right = value,
bottom = value;
EdgeInsets.all() は only() と違い、引数を1つだけとって4方向全てに対して余白を設定することができます。
return Row(
children: [
Container(
,margin: EdgeInsets.all(50),,
color: Colors.green,
width: 100,
height: 100,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
],
);
緑色のContainerの4方向にmarginが設定されているので、赤色のContainerにmarginを設定していなくとも全方向に余白を設定することができます。
/// Creates insets with symmetrical vertical and horizontal offsets.
///
/// {@tool snippet}
///
/// Eight pixel margin above and below, no horizontal margins:
///
/// ```dart
/// const EdgeInsets.symmetric(vertical: 8.0)
/// ```
/// {@end-tool}
const EdgeInsets.symmetric({
double vertical = 0.0,
double horizontal = 0.0,
}) : left = horizontal,
top = vertical,
right = horizontal,
bottom = vertical;
EdgeInsets.symmetric() は引数を2つとることができ、縦方向、横方向それぞれの方向へ余白を設定することができます。
return Column(
children: List.generate(
5,
(index) => Card(
color: Colors.white10,
,margin: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),,
child: Container(height: 100),
),
),
);
vertical: 10.0 で上下方向に10.0ずつ余白を設定しています。これは EdgeInsets.only(top: 10.0, bottom: 10.0) と同義です。
もう1つの引数 horizontal: 20.0 で左右方向に20.0ずつ余白を設定しています。これは EdgeInsets.only(left: 20.0, right: 20.0) と同義です。
EdgeInsets.only() で上下(または左右)に同じ値を設定する場合は、こちらを使う方が可読性含めてよいと思います。
/// Creates insets from offsets from the left, top, right, and bottom.
const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
EdgeInsets.fromLTRB() は引数を4つとり、各方向への余白を設定できます。
EdgeInsets.only() と違う点は、名前なし引数であること、余白0の場合も明示的に引数を指定しなければいけない点です。
return Column(
children: List.generate(
5,
(index) => Card(
color: Colors.redAccent,
,margin: EdgeInsets.fromLTRB(0.0, 20.0, 30.0, 5.0),,
child: Container(height: 100),
),
),
);
各方向へ自由に余白を設定できます。
個人的にはこのような場合は EdgeInsets.only() を使う方がよいと思うので利用シーンが思いつかないです。。
この記事を参考にしてくれるレベル感の読者に向けて、知っておくと便利なTipsを書いておきます。
なんとEdgeInsetsは加減算することができます。
EdgeInsets verticalMargin = EdgeInsets.symmetric(vertical: 10.0);
EdgeInsets sideMargin = EdgeInsets.symmetric(horizontal: 20.0);
return Column(
children: List.generate(
5,
(index) => Card(
color: Colors.orangeAccent,
margin: verticalMargin + sideMargin,
child: Container(height: 100),
),
),
);
「左右のmarginだけは固定で決まっている」みたいなユースケースでは、変数として EdgeInsets.symmetric を定義しておいて、その変数+上下のmarginを自由に設定する みたいな使い方ができそうですね。
Flutterの開発をしている方はAndroid StudioかVSCodeを使用していると思うのですが、エディタでEdgeInsetsの仕様を確認することができます。
クラスやメソッドなどを ⌘+クリック すると、その宣言にジャンプすることができるのですが、Flutter側で実装されているEdgeInsetsなども同様にジャンプすることができます。
↑ Android StudioでEdgeInsetsの仕様を確認できる
↑ VSCodeでEdgeInsetsの仕様を確認できる
実装に困った時や初めてのWidgetを扱う時は、一度宣言に飛ぶことで仕様を確認することをオススメします。
今回ご紹介したものは全てEdgeInsetsの宣言に飛ぶことで理解できる内容なので、日頃から扱うWidgetの仕様を確認すると理解が捗ると思います。
若干EdgeInsetsの話からは逸れてしまいますが、marginの幅が固定なのであれば const修飾子をつける クセをつけておくとよいです。
メリットとしては、ユーザーのアクションによってデータが変更される時にアプリが自動で表示されているWidgetを再構成して見た目を更新してくれるのですが、その 描画のパフォーマンスを改善 することができます(劇的な改善にはなりません)
Flutterの初期プロジェクトでも、FABをタップすることでsetState()を呼び出してcountを+1し、それに応じてbuildメソッドが再度呼び出されてカウント数が更新されます(かなり雑に例をあげました)
const修飾子 をつけてあげることで、Widgetの再描画の対象から外されるのでパフォーマンスの改善につなげることができます。
return Column(
children: List.generate(
5,
(index) => Card(
color: Colors.redAccent,
margin: const EdgeInsets.fromLTRB(0.0, 20.0, 30.0, 5.0),
child: Container(height: 100),
),
),
);
EdgeInsetsの引数が固定の場合は問答無用で const をつけてあげましょう。(他のWidgetにも言えることなので気になる方は調べてください)
この記事では、marginの基本的な設定の仕方からEdgeInsetsのいろいろな使い方をご紹介しました。
知識0の状態から学んでいる人にとっては、とりあえず EdgeInsets.only() で動いてしまうのでそのままにしがちな内容だと思います。そういう方の学びになればと思います。