[Flutter] ピアノアプリ作る。

こんにちは。
皆さんはピアノを弾くこと。好きなんですか？
ピアノアプリ作ってみました。

sf2 file 必要です。
作って見た後、Flutterのまた感動受けました。!

![image.png](https://qiita-image-store.s3.amazonaws.com/0/137182/41924dc2-f1ea-4866-978c-13e103d214c8.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137182/685ef486-3905-e3f4-596e-30eacb8be5dd.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137182/3d74c8d6-c04d-9b77-ad67-4e3ddbab0560.png)

[Screenshot_1553007766.png](https://qiita-image-store.s3.amazonaws.com/0/137182/8c378132-decc-eb9b-edd5-d904fb9227d2.png)

```dart:main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_midi/flutter_midi.dart';
import 'package:tonic/tonic.dart';

class PianoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: PianoMain(),
    );
  }
}

class PianoMain extends StatefulWidget {
  @override
  _PianoMainState createState() => _PianoMainState();
}

class _PianoMainState extends State<PianoMain> {
  double _widthRatio = 0.0;
  bool _showLabels = true;

  double get keyWidth => 80 + (80 * _widthRatio);

  @override
  void initState() {
    FlutterMidi.unmute();
    rootBundle.load('assets/sound/Wii_Grand_Piano.sf2').then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: "GeneralUser.sf2");
    });
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("piano App"),
      ),
      drawer: Drawer(
        child: SafeArea(
            child: ListView(
          children: <Widget>[
            Container(
              height: 20.0,
            ),
            ListTile(
              title: Text("Change Width"),
            ),
            Slider(
              activeColor: Colors.white,
              inactiveColor: Colors.red,
              min: 0.0,
              max: 1.0,
              value: _widthRatio,
              onChanged: (double value) => setState(() => _widthRatio = value),
            ),
            Divider(),
            ListTile(
              title: Text("Show Labels"),
              trailing: Switch(
                  value: _showLabels,
                  onChanged: (bool value) {
                    setState(() {
                      _showLabels = value;
                    });
                  }),
            ),
            Divider(),
          ],
        )),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          final int i = index * 12;

          return SafeArea(
            child: Stack(
              children: <Widget>[
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    _buildKey(24 + i, false),
                    _buildKey(26 + i, false),
                    _buildKey(28 + i, false),
                    _buildKey(29 + i, false),
                    _buildKey(31 + i, false),
                    _buildKey(33 + i, false),
                    _buildKey(35 + i, false),
                  ],
                ),
                Positioned(
                  left: 0.0,
                  right: 0.0,
                  bottom: 100,
                  top: 0.0,
                  child: Row(
                    mainAxisSize: MainAxisSize.min,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Container(
                        width: keyWidth * 0.5,
                      ),
                      _buildKey(25 + i, true),
                      _buildKey(27 + i, true),
                      Container(
                        width: keyWidth,
                      ),
                      _buildKey(30 + i, true),
                      _buildKey(32 + i, true),
                      _buildKey(34 + i, true),
                      Container(
                        width: keyWidth * 0.5,
                      ),
                    ],
                  ),
                )
              ],
            ),
          );
        },
        itemCount: 7,
        scrollDirection: Axis.horizontal,
      ),
    );
  }

  Widget _buildKey(int i, bool param1) {
    final pitchName = Pitch.fromMidiNumber(i).toString();
    final pianoKey = Stack(
      children: <Widget>[
        Semantics(
          button: true,
          hint: pitchName,
          child: Material(
            borderRadius: BorderRadius.only(
                bottomLeft: Radius.circular(10.0),
                bottomRight: Radius.circular(10.0)),
            color: param1 ? Colors.black : Colors.white,
            child: InkWell(
              borderRadius: BorderRadius.only(
                  bottomLeft: Radius.circular(10.0),
                  bottomRight: Radius.circular(10.0)),
              highlightColor: Colors.grey,
              onTap: () {},
              onTapDown: (_) => FlutterMidi.playMidiNote(midi: i),
            ),
          ),
        ),
        Positioned(
            left: 0.0,
            right: 0.0,
            bottom: 20.0,
            child: _showLabels
                ? Text(
                    pitchName,
                    textAlign: TextAlign.center,
                    style:
                        TextStyle(color: !param1 ? Colors.black : Colors.white),
                  )
                : Container()),
      ],
    );
    if (param1) {
      return Container(
        width: keyWidth,
        color: Colors.black,
        margin: EdgeInsets.symmetric(horizontal: 2.0),
        padding: EdgeInsets.symmetric(horizontal: keyWidth * 0.1),
        child: Material(
          elevation: 6.0,
          borderRadius: BorderRadius.only(
              bottomLeft: Radius.circular(10.0),
              bottomRight: Radius.circular(10.0)),
          shadowColor: Color(0x802196f3),
          child: pianoKey,
        ),
      );
    }

    return Container(
      width: keyWidth,
      color: Colors.white,
      margin: EdgeInsets.symmetric(horizontal: 2.0),
      child: pianoKey,
    );
  }
}
```