2021/3/6 : Flutter : Write your first Flutter app on the webの訳パート2

<<前のページへ

 

Step 2.5: Launch Dart DevTools

How do you debug a Flutter web app? It’s not too different from debugging any Flutter app. You want to use Dart DevTools! (Not to be confused with Chrome DevTools.)

Flutter Webアプリをどのようにデバッグしますか? Flutterアプリのデバッグとそれほど違いはありません。 Dart DevToolsを使用したいですよね! (Chrome DevToolsと混同しないでください。)


Our app currently has no bugs, but let’s check it out anyway.

ここまでこのコードラボのサンプルコードにバグはありません。しかしとりあえず試してみましょう。

 

The following instructions for launching DevTools applies to any workflow, but there is a short cut if you’re using IntelliJ. See the tip at the end of this section for more information.

DevToolsを起動するための次の手順はすべてのワークフローに適用されますが、IntelliJを使用している場合はショートカットがあります。 詳細については、このセクションの最後にあるヒントを参照してください。

 

1.Run the app.

アプリを実行します。

 

If your app isn’t currently running, launch it. Select the Chrome device from the pull down and launch it from your IDE or, from the command line, use flutter run -d chrome,

アプリが実行されていない場合、実行します。プルダウンからChromeデバイスを選びます。そしてIDEから実行します。またはコマンドラインからの場合、

flutter run -d chrome

コマンドを実行します。


2.Get the web socket info for DevTools.

DevToolsのWebソケット情報を取得します。

 

At the command line, or in the IDE, you should see a message stating something like the following:

コマンドラインまたはIDEで、次のようなメッセージが表示されます。

Launching lib/main.dart on Chrome in debug mode...
Building application for the web...                                11.7s
Attempting to connect to browser instance..
Debug service listening on **ws://127.0.0.1:54998/pJqWWxNv92s=**

Copy the address of the debug service, shown in bold. You will need that to launch DevTools.

太字で示されているデバッグサービスのアドレスをコピーします。 DevToolsを起動するためにそれが必要になります。


3.Ensure that DevTools is installed.

DevToolsがインストールされていることを確認します。

 

If you are using an IDE, make sure you have the Flutter and Dart plugins set up, as described in the VS Code and Android Studio and IntelliJ pages.

IDEを使用している場合は、VSCodeとAndroidStudioおよびIntelliJのページで説明されているように、FlutterプラグインとDartプラグインが設定されていることを確認してください。

 

If you are working at the command line, launch the DevTools server as explained in the DevTools command line page.

コマンドラインで作業している場合は、DevToolsコマンドラインページの説明に従ってDevToolsサーバーを起動します。


4.Connect to DevTools.

DevToolsに接続します。

 

When DevTools launches, you should see something like the following:

DevToolsを起動すると、次のようなメッセージが表示されます。

Serving DevTools at http://127.0.0.1:9100

Go to this URL in a Chrome browser. You should see the DevTools launch screen. It should look like the following:

ChromeブラウザでこのURLにアクセスします。 DevToolsの起動画面が表示されます。 次のようになります。

Screenshot of the DevTools launch screen


5.Connect to running app.

実行中のアプリに接続します。

 

Under Connect to a running site, paste the ws location that you copied in step 2, and click Connect. You should now see Dart DevTools running successfully in your Chrome browser:

[Connect to a running site]で、step2でコピーしたwsのlocationを貼り付け、[Connect]をクリックします。 これで、ChromeブラウザでDartDevToolsが正常に実行されていることがわかります。

Screenshot of DevTools running screen

Congratulations, you are now running Dart DevTools!

おめでとうございます。DartDevToolsを実行しています。


Note: This is not the only way to launch DevTools. If you are using IntelliJ, you can open DevTools by going to Flutter Inspector -> More Actions -> Open DevTools:

DevToolsを起動する方法はこれだけではありません。もしあなたが、IntelliJを使っているなら、

Flutter Inspector -> More Actions -> Open DevTools:

でDevToolsを開くこともできます。

Screenshot of Flutter inspector with DevTools menu


1.Set a breakpoint.

ブレークポイントを設定します。

 

Now that you have DevTools running, select the Debugger tab in the blue bar along the top.

DevToolsを実行しているので、上部にある青いバーの[Debugger]タブを選択します。

 

The debugger pane appears and, in the lower left, see a list of libraries used in the example.

デバッガーペインが表示され、左下に、サンプルで使用されているライブラリのリストが表示されます。

 

Select signin/main.dart to display your Dart code in the center pane.

signin / main.dartを選択して、中央のペインにDartコードを表示します。

Screenshot of the DevTools debugger


2.Set a breakpoint.

ブレークポイントを設定します。

 

In the Dart code, scroll down to where progress is updated:

Dartコードで、進行状況が更新される場所まで下にスクロールします。

    for (var controller in controllers) {
      if (controller.value.text.isNotEmpty) {
        progress += 1 / controllers.length;
      }
    }

Place a breakpoint on the line with the for loop by clicking to the left of the line number.

行番号の左側をクリックして、forループのある行にブレークポイントを配置します。

 

 

The breakpoint now appears in the Breakpoints section to the left of the window.

これで、ウィンドウの左側の[ブレークポイント]セクションにブレークポイントが表示されます。


3.Trigger the breakpoint.

ブレークポイントをトリガーします。

 

In the running app, click one of the text fields to gain focus.

実行中のアプリで、テキストフィールドの1つをクリックしてフォーカスを取得します。

 

The app hits the breakpoint and pauses.

アプリはブレークポイントに到達して一時停止します。

 

In the DevTools screen, you can see on the left the value of progress, which is 0.

DevTools画面では、左側にprogressの値である0が表示されます。

 

This is to be expected, since none of the fields are filled in.

どのフィールドにも入力されていないため、これは期待通りの挙動です。


4.Resume the app.

アプリを再開します。

 

Resume the app by clicking the green Resume button in the DevTools window.

DevToolsウィンドウの緑色の[Resume]ボタン(再開)をクリックして、アプリを再開します。


5.Delete the breakpoint.

ブレークポイントを削除します。

 

Delete the breakpoint by clicking it again, and resume the app.

ブレークポイントをもう一度クリックして削除し、アプリを再開します。

 

This gives you a tiny glimpse of what is possible using DevTools, but there is lots more! For more information, see the DevTools documentation.

これにより、DevToolsを使用して何ができるかを少し垣間見ることができますが、他にもたくさん機能があります。 詳細については、DevToolsのドキュメントを参照してください。

参考

https://flutter.dev/docs/get-started/codelab-web#step-25-launch-dart-devtools


Step 3: Add animation for sign in progress

It’s time to add animation!

アニメーションを追加する時が来ました!

 

you’ll create the animation for the LinearProgressIndicator at the top of the sign in area.

サインイン領域の上部にLinearProgressIndicatorのアニメーションを作成します。

 

The animation has the following behavior:

アニメーションの動作は次のとおりです。

 

  • When the app starts, a tiny red bar appears across the top of the sign in area.
  • When one text field contains text, the red bar turns orange and animates 0.15 of the way across the sign in area.
  • When two text fields contain text, the orange bar turns yellow and animates half of the way across the sign in area.
  • When all three text fields contain text, the orange bar turns green and animates all the way across the sign in area. Also, the Sign up button becomes enabled.
  • アプリが起動すると、サインイン領域の上部に小さな赤いバーが表示されます。
  • 1つのテキストフィールドにテキストが含まれている場合、赤いバーがオレンジ色に変わり、サインイン領域全体で0.15の距離をアニメーション化します。
  • 2つのテキストフィールドにテキストが含まれている場合、オレンジ色のバーが黄色に変わり、サインイン領域の半分をアニメーション化します。
  • 3つのテキストフィールドすべてにテキストが含まれている場合、オレンジ色のバーが緑色に変わり、サインイン領域全体でアニメーション化されます。 また、サインアップボタンが有効になります。

1.Add an AnimatedProgressIndicator.

AnimatedProgressIndicatorウィジェットを追加します。

 

At the bottom of the file, add this widget:

ファイルの最後に、次のウィジェットを追加します。

class AnimatedProgressIndicator extends StatefulWidget {
  final double value;

  AnimatedProgressIndicator({
    @required this.value,
  });

  @override
  State<StatefulWidget> createState() {
    return _AnimatedProgressIndicatorState();
  }
}

class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<Color> _colorAnimation;
  Animation<double> _curveAnimation;

  void initState() {
    super.initState();
    _controller = AnimationController(
        duration: Duration(milliseconds: 1200), vsync: this);

    var colorTween = TweenSequence([
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.red, end: Colors.orange),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.yellow, end: Colors.green),
        weight: 1,
      ),
    ]);

    _colorAnimation = _controller.drive(colorTween);
    _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
  }

  @override
  void didUpdateWidget(oldWidget) {
    super.didUpdateWidget(oldWidget);
    _controller.animateTo(widget.value);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) => LinearProgressIndicator(
        value: _curveAnimation.value,
        valueColor: _colorAnimation,
        backgroundColor: _colorAnimation.value.withOpacity(0.4),
      ),
    );
  }
}

The didUpdateWidget function updates the AnimatedProgressIndicatorState whenever AnimatedProgressIndicator changes.

didUpdateWidgetメソッドは、AnimatedProgressIndicatorが変更されるたびにAnimatedProgressIndicatorStateをアップデートします。


2.Use the new AnimatedProgressIndicator.

新しいAnimatedProgressIndicatorを使います。

 

Then, replace the LinearProgressIndicator in the Form with this new AnimatedProgressIndicator:

FormのLinearProgressIndicatorの箇所を、今定義したAnimatedProgressIndicatorに置き換えます。

...
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          AnimatedProgressIndicator(value: _formProgress), // NEW
          Text('Sign up', style: Theme.of(context).textTheme.headline4),
          Padding(
...

This widget uses an AnimatedBuilder to animate the progress indicator to the latest value.

このウィジェットはAnimatedBuilderウィジェットを使って、最新の値によってプログレスインジケーターをアニメーション化します。

3.Run the app.

アプリを実行します。

Type anything into the three fields to verify that the animation works, and that clicking the Sign up button brings up the Welcome screen.

三つのテキストフィールドに何かを入力して、アニメーションが機能することを確認してみましょう。そしてSign upボタンをクリックしてWelcomeスクリーンに進みましょう。


Complete sample

import 'package:flutter/material.dart';

void main() => runApp(SignUpApp());

class SignUpApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        '/': (context) => SignUpScreen(),
        '/welcome': (context) => WelcomeScreen(),
      },
    );
  }
}

class SignUpScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: Center(
        child: SizedBox(
          width: 400,
          child: Card(
            child: SignUpForm(),
          ),
        ),
      ),
    );
  }
}

class WelcomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Welcome!', style: Theme.of(context).textTheme.headline2),
      ),
    );
  }
}

class SignUpForm extends StatefulWidget {
  @override
  _SignUpFormState createState() => _SignUpFormState();
}

class _SignUpFormState extends State<SignUpForm> {
  final _firstNameTextController = TextEditingController();
  final _lastNameTextController = TextEditingController();
  final _usernameTextController = TextEditingController();

  double _formProgress = 0;

  void _updateFormProgress() {
    var progress = 0.0;
    var controllers = [
      _firstNameTextController,
      _lastNameTextController,
      _usernameTextController
    ];

    for (var controller in controllers) {
      if (controller.value.text.isNotEmpty) {
        progress += 1 / controllers.length;
      }
    }

    setState(() {
      _formProgress = progress;
    });
  }

  void _showWelcomeScreen() {
    Navigator.of(context).pushNamed('/welcome');
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      onChanged: _updateFormProgress,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          AnimatedProgressIndicator(value: _formProgress),
          Text('Sign up', style: Theme.of(context).textTheme.headline4),
          Padding(
            padding: EdgeInsets.all(8.0),
            child: TextFormField(
              controller: _firstNameTextController,
              decoration: InputDecoration(hintText: 'First name'),
            ),
          ),
          Padding(
            padding: EdgeInsets.all(8.0),
            child: TextFormField(
              controller: _lastNameTextController,
              decoration: InputDecoration(hintText: 'Last name'),
            ),
          ),
          Padding(
            padding: EdgeInsets.all(8.0),
            child: TextFormField(
              controller: _usernameTextController,
              decoration: InputDecoration(hintText: 'Username'),
            ),
          ),
          TextButton(
            style: ButtonStyle(
              foregroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
                return states.contains(MaterialState.disabled) ? null : Colors.white;
              }),
              backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
                return states.contains(MaterialState.disabled) ? null : Colors.blue;
              }),
            ),
            onPressed: _formProgress == 1 ? _showWelcomeScreen : null,
            child: Text('Sign up'),
          ),
        ],
      ),
    );
  }
}

class AnimatedProgressIndicator extends StatefulWidget {
  final double value;

  AnimatedProgressIndicator({
    @required this.value,
  });

  @override
  State<StatefulWidget> createState() {
    return _AnimatedProgressIndicatorState();
  }
}

class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<Color> _colorAnimation;
  Animation<double> _curveAnimation;

  void initState() {
    super.initState();
    _controller = AnimationController(
        duration: Duration(milliseconds: 1200), vsync: this);

    var colorTween = TweenSequence([
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.red, end: Colors.orange),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.yellow, end: Colors.green),
        weight: 1,
      ),
    ]);

    _colorAnimation = _controller.drive(colorTween);
    _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
  }

  void didUpdateWidget(oldWidget) {
    super.didUpdateWidget(oldWidget);
    _controller.animateTo(widget.value);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) => LinearProgressIndicator(
        value: _curveAnimation.value,
        valueColor: _colorAnimation,
        backgroundColor: _colorAnimation.value.withOpacity(0.4),
      ),
    );
  }
}

 

Observations

  • You can use an AnimationController to run any animation.

AnimationControllerウィジェットを使ってアニメーションを実装することができます。

 

  • AnimatedBuilder rebuilds the widget tree when the value of an Animation changes.

Animationの値が変わるたびにAnimatedBuilderがウィジェットツリーをリビルドします。

 

  • Using a Tween, you can interpolate between almost any value, in this case, Color.

Tweenを使うと、ほとんど全ての値の変化の間を補間することができます。今回はColorの値の変化の間を補間しています。

参考

https://flutter.dev/docs/get-started/codelab-web#step-3-add-animation-for-sign-in-progress


What next?

Congratulations! You have created your first web app using Flutter!

おめでとうございます。Flutterで初めてのwebアプリを作ることができました。

 

If you’d like to continue playing with this example, perhaps you could add form validation.

この例を引き続き使用する場合は、フォームの検証を追加できます。

 

For advice on how to do this, see the Building a form with validation recipe in the Flutter cookbook.

これを行う方法のアドバイスについては、Flutterクックブックのvalidationを使用したフォームの作成のレシピを参照してください。

 

For more information on Flutter web apps, Dart DevTools, or Flutter animations, see the following:

Flutter Webアプリ、Dart DevTools、またはFlutterアニメーションの詳細については、以下を参照してください。

コメントを残す

メールアドレスが公開されることはありません。