Open7

Flutter開発中の覚え書きや気付きをメモしていく

renoinnrenoinn

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: 'プレースホルダー',
  ),
);
renoinnrenoinn

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();
renoinnrenoinn

TextFormFieldvalidationでエラー判定するときに、メッセージが長くても途切れないようにする。

TextFormField(
  decoration: InputDecoration(
    hintText: 'プレースホルダー',
    errorMaxLines: 2, // エラーメッセージの長さに応じて調整
  ),
);
renoinnrenoinn

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,
  ),
)
renoinnrenoinn

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('画像を読み込めませんでした'),
    ));
  }
}