webの管理画面から、csvファイルをアップして、 一時的にstorageにアップして、functionsなどで処理するような 機能を想定しています。
ソースみると、そもそもhtml.Blobで型チェックしてるんですよね。
なぜ引数の型がdynamicなのか?
webの管理画面から、csvファイルをアップして、 一時的にstorageにアップして、functionsなどで処理するような 機能を想定しています。
ソースみると、そもそもhtml.Blobで型チェックしてるんですよね。
なぜ引数の型がdynamicなのか?
binフォルダを作ってそこにdartファイルを入れて実行すればいいだけ
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(); } }
プロジェクトの構成などはこちら
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, )), ); } }
書き換える前
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は不要です。