Open7
Flutter開発中の覚え書きや気付きをメモしていく
「ローカルステートなのでStateNotifierとかに切り出すほどじゃなくStatefullWidgetで十分そうだけど、ビューとロジックのコードは分離したい」という時に、便利な書き方。
InputDecorationとかは毎度書くと長くなりがちなので、いくつかベースになるものを用意してcopyWithすると楽。
const roundedBorderDecoration = InputDecoration(
hintStyle: TextStyle(color: Colors.gray),
counter: SizedBox(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.gray),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.gray, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
);
TextFormField(
decoration: roundedBorderDecoration.copyWith(
hintText: 'プレースホルダー',
),
);
assetsから画像データを読み込む。リサイズが必要な場合は image/image
パッケージのImage
を使う。
// 普通に読み込む場合
final byteData = await rootBundle.load('assets/images/icon.png');
final codec = await ui.instantiateImageCodec(byteData.buffer.asUint8List());
final frameInfo = await codec.getNextFrame();
final image = frameInfo.image;
// 読み込んでリサイズする場合
import 'package:image/image.dart' as image;
...
final byteData = await rootBundle.load('assets/images/icon.png');
final baseSizeImage = image.decodeImage(byteData.buffer.asUint8List());
final resizeImage = image.copyResize(baseSizeImage, height: iconSize.height.toInt(), width: iconSize.width.toInt());
final codec = await ui.instantiateImageCodec(image.encodePng(resizeImage));
final frameInfo = await codec.getNextFrame();
final imageData = frameInfo.image;
canvasに貼り付ける
final pictureRecorder = ui.PictureRecorder();
final canvas = Canvas(pictureRecorder);
final paintImage = Paint()..isAntiAlias = true;
canvas.save();
canvas.drawImage(image, Offset((canvasSize - image.width) / 2, (canvasSize - image.height) / 2), paintImage);
canvas.restore();
TextFormField
のvalidation
でエラー判定するときに、メッセージが長くても途切れないようにする。
TextFormField(
decoration: InputDecoration(
hintText: 'プレースホルダー',
errorMaxLines: 2, // エラーメッセージの長さに応じて調整
),
);
TextFormField
でbuildCounterで文字数カウントをカスタマイズしつつ、エラーメッセージも表示したいサンプル。
buildCounter
を指定すると、エラーメッセージ部分もごっそり置き換えてしまうので、FormFieldState
経由でエラーを検知する。エラーがある場合は、FormFieldState.errorText
で、設定したvalidatorで返したエラーメッセージを取得できる。
final _formKey = GlobalKey<FormState>();
final _commentKey = GlobalKey<FormFieldState>();
...
Form(
key: _formKey,
child: TextFormField(
key: _commentKey,
maxLines: 4,
maxLength: 1000,
style: const TextStyle(fontSize: 12.0),
decoration: roundedDecoration.copyWith(
hintText: 'プレースホルダー',
),
buildCounter: (_, {currentLength, maxLength, isFocused}) => Container(
padding: const EdgeInsets.only(left: 16.0),
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_commentKey.currentState.hasError)
Text(
_commentKey.currentState.errorText,
style: TextStyle(color: Colors.red[700], fontSize: 14.0), // FIXME できればデフォルトのテーマをTheme.ofとかで引っ張ってきたいけど、上手くいく方法が見つけられなかった
),
Text(
currentLength.toString() + '/' + maxLength.toString(),
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
),
],
),
),
validator: _commentValidator,
),
)
enum like class
image_pickerで取得した画像をTempディレクトリに保存しつつ、圧縮する。
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
List<File> photos = [];
final imagePicker = ImagePicker();
final image = await imagePicker.getImage(source: source); // 一応、ここでitemQualityを指定することもできる
if (image != null) {
try {
final tempDirectory = await getTemporaryDirectory();
final outPath = tempDirectory.path + basenameWithoutExtension(image.path) + '_comp.jpg';
final result = await FlutterImageCompress.compressAndGetFile(
image.path,
outPath,
minWidth: 1440,
minHeight: 1440,
quality: 60,
format: CompressFormat.jpeg,
);
photos.add(result);
} catch (e) {
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('画像を読み込めませんでした'),
));
}
}