FileUploadInputElement.filesのhtml.FileはFirebaseStorageのputBlobの引数として使える

https://github.com/na8esin/flutter_file_picker_sample/blob/main/lib/FileUploadInputElement_to_storage.dart

webの管理画面から、csvファイルをアップして、 一時的にstorageにアップして、functionsなどで処理するような 機能を想定しています。

ソースみると、そもそもhtml.Blobで型チェックしてるんですよね。

https://github.com/FirebaseExtended/flutterfire/blob/dc609a72cbf95e3519bb6e5bf26476b7bd2359b3/packages/firebase_storage/firebase_storage_web/lib/src/reference_web.dart#L170

なぜ引数の型がdynamicなのか?

flutter test lab integration_test 動画が取れる

flutter.dev

とりあえず、この辺りを読んでおけば大丈夫ですが、 ハマったポイントを書いておきます。

MainActivityTestはjavaでも大丈夫

MainActivityがktだったので合わせようと思いましたが、 いざtest labで実行するときにMainActivityが見つけられなかったので、 javaにしてMainActivityと別にしました。

test lab missing activity rule

ところどころFuture.delayedするとうまく動画に映る

矢印が移動しながら回転し、左側の横幅がそれに伴って変わる

NavigationRailだとカスタマイズが効かないので、 左メニューを自作できないかと試行錯誤中です。

今回は、メニューを開いたり閉じたりする部分を作ってみました。

NavigationRailも中身はConstrainedBoxなので、 minWidthをAnimationControllerで操作します。

import 'dart:math' as math;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

/// 矢印が移動しながら回転し、左側の領域のサイズが変わる
void main() {
  runApp(ProviderScope(
      child: MaterialApp(
          home: Scaffold(
    body: MyHomePage(),
  ))));
}

class MyHomePage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    const _iconSize = 32.0;
    final controller = useAnimationController(
      duration: const Duration(milliseconds: 250),
    );
    useListenable(controller);
    return Row(
      children: [
        ConstrainedBox(
          // 160.0くらいが見た目的にいい感じなので、
          // (1 * 4 + 1) * 32 = 5 * 32
          constraints:
              BoxConstraints(minWidth: (controller.value * 4 + 1) * _iconSize),
          child: Align(
            alignment: Alignment.topLeft,
            child: FractionalTranslation(
              // Tween<Offset>を使うと指定が簡単になるが、
              // 最初は理解が難しくなるので使わない
              translation: Offset(controller.value * 4, 0),
              child: InkWell(
                onTap: () {
                  if (controller.isDismissed) {
                    controller.forward();
                  } else if (controller.isCompleted) {
                    controller.reverse();
                  }
                },
                child: Transform.rotate(
                  angle: controller.value * math.pi,
                  child: const Icon(
                    Icons.arrow_right,
                    size: _iconSize,
                  ),
                ),
              ),
            ),
          ),
        ),
        VerticalDivider(thickness: 1, width: 1),
        Expanded(
          child: Center(
            child: MyRightWidget(),
          ),
        )
      ],
    );
  }
}

class MyRightWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

20210320103201 20210320103206

プロジェクトの構成などはこちら

https://github.com/na8esin/flutter_side_menu_study/blob/main/lib/study/animation/FractionalTranslation/Rotating_arrow_and_left_menu.dart

useAnimationControllerとFractionalTranslationで矢印を右に移動する

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

/// 矢印が右に移動するだけ
void main() {
  runApp(ProviderScope(
      child: MaterialApp(
          home: Scaffold(
    body: MyHomePage(),
  ))));
}

class MyHomePage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(
      duration: const Duration(milliseconds: 5000),
    );
    useListenable(controller);
    controller.forward();
    return Container(
      alignment: Alignment.centerLeft,
      child: FractionalTranslation(
          translation: Offset(controller.value, 0),
          child: Icon(
            Icons.arrow_right,
            size: 32,
          )),
    );
  }
}

Flutter samples animations 03_animation_controllerをflutter_hooksで書き換える

書き換える前

https://github.com/flutter/samples/blob/master/animations/lib/src/basics/03_animation_controller.dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() => runApp(MaterialApp(
      home: AnimationControllerDemo(),
    ));

class AnimationControllerDemo extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: Duration(seconds: 1));
    // https://github.com/rrousselGit/flutter_hooks/issues/204
    // を見て気づいた
    useListenable(controller);

    return Scaffold(
      appBar: AppBar(
        title: Text('Animation Controller'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ConstrainedBox(
              constraints: BoxConstraints(maxWidth: 200),
              child: Text(
                '${controller.value.toStringAsFixed(2)}',
                style: Theme.of(context).textTheme.headline3,
                textScaleFactor: 1 + controller.value,
              ),
            ),
            ElevatedButton(
              child: Text('animate'),
              onPressed: () {
                if (controller.status == AnimationStatus.completed) {
                  controller.reverse();
                } else {
                  controller.forward();
                }
              },
            )
          ],
        ),
      ),
    );
  }
}

もともとのソースの下記の部分の書き換えがわからずに苦労しました。 特にAnimationController.valueが変更されるたびにsetStateみたいなものを実行するのがわかりませんでした。

controller = AnimationController(vsync: this, duration: _duration)
      ..addListener(() {
        setState(() {});
      });

ですが、冷静にflutter_hooksのissueを見ていたらuseListenableを使えばいいことに気づきました。

ただ、変更が起きるところをAnimatedBuilderを囲えばuseListenableは不要です。