Dyuichi Blog

Flutterでのアプリ開発メモ

基本的に自分用のメモ書きです.

Flutterアプリ開発全般

以下の記事がめちゃくちゃ参考になる.

特に以下のコマンドについてのページは覚えておきたい.

開発Tips

親Widgetから子Widgetへの値・関数の受け渡し

以下に実装例を示す.重要な箇所にはコメントを入れた.

dartclass ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  // 親から子に渡される変数
  int counterParent = 0;
  final String buttonNameParent = 'Button Name'; 

  // 親から子に渡される関数
  void addCountParent() {
    setState(() {
      counter++; 
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      // 子クラスの引数に渡す
      body: ChildWidget(
        counterChild: counterParent,
        buttonNameChild: buttonNameParent,
        addCountChild: addCountParent,
      ),
    );
  }
}

class ChildWidget extends StatefulWidget {
  // 親クラスから受け取りたい変数をコンストラクタで受け取る
  const ChildWidget({
    Key? key, 
    required this.counterChild,
    required this.buttonNameChild,
    required this.addCountChild,
  });

  // 親クラスから受け取りたい変数宣言
  final int counterChild;
  final String buttonNameChild;
  final Function addCountChild;

  
  State<ChildWidget> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildtWidget> {
  
  // widget.変数(関数) の形で使う
  Widget build(BuildContext context) {
    // 以下のreturn文をいい感じにしてください.
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Text(
          'Counter: ${widget.counterChild}',
          style: TextStyle(fontSize: 24),
        ),
        ElevatedButton(
          onPressed: () => widget.addCountChild(),
          child: Text(widget.buttonNameChild),
        ),
      ],
    );
  }
}

ListViewで無限スクロールを実現する方法

以下に実装例を示す.以下の実装では,スクロール限界までスクロールされたときに,メソッド(apiにGETリクエストを送信してリソースを取得)を呼び出して,リソースの追加を行っている.ほかにも無限スクロールを実現する方法はいろいろある.

dartclass _SampleWidgetState extends State<SampleWidget> {
  List<Content> contents = [];
  ScrollController _scrollController = ScrollController();
  int currentPage = 1;

  
  void initState() {
    super.initState();

    // 最初のコンテンツ取得
    _submission();

    // 限界までスクロールされたかどうかの判定
    _scrollController.addListener(() {
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        _submissionMore();
      }
    });
  }

  
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  void _submission() async {
    final results = await getContents(1);
    setState(() {
      _currentPage = 1;
      contents = results;
    });
  }

  void _submissionMore() async {
    // 限界までスクロールされた時の追加コンテンツ取得
    final results = await getContents(currentPage + 1);
    setState(() {
      _currentPage++;
      contents.addAll(results);
    });
  }

  // ...
  
  Widget build(BuildContext context) {
    // ...

    ListView(
      controller: _scrollController,  // _scrollControllerを渡す
      children: contents.map<ContentContainer>((value) {
        return ContentContainer(value: value);
      }).toList(),
    ),

    // ...
  }
}

// APIを呼び出して,コンテンツ取得
Future<List<Content>> getContents(int page) async {
  // ...

  final uri = Uri.https('example.com', '/api/v1', {
    'offset': page.toString(),
  });

  // ...

  return contents;
}
参考

map関数でindexを取得する

Listをmap関数でListViewのchildrenに,List型をmap関数で渡すことがある(以下みたいに).

dartList<Item> listItem = [];
ListView(
  children: listItem.map<Item>((value) {
    return MyWidget(item: value);
  }).toList(),
)

ここで,各子WidgetであるMyWidgetに対してlistItemのindexを渡したいときがあったりする(ランキングで順位を表示させたいときとか).

この時は,以下のようにasMap関数を使うとよい.

dartList<Item> listItem = [];
ListView(
  children: listItem.asMap().entries.map<Item>((entry) {
    return MyWidget(item: entry.value, index: entry.key);
  }).toList(),
)
参考

timezone

以下のように,toLocalメソッドを使うと,ローカルタイムゾーンにできる.ただし注意点として,エミュレーターでは,UTCタイムゾーンが使われていたりする(何かしらの方法で設定変更できる?).

dartDateTime.now().toLocal()
参考

ブラウザ起動

WebView埋め込み型を実現したければ,以下のように実装するだけでよい(ドキュメント通りに実装するとよい).

dartcontroller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..setBackgroundColor(const Color(0x00000000))
  ..setNavigationDelegate(
    NavigationDelegate(
      onProgress: (int progress) {
        // Update loading bar.
      },
      onPageStarted: (String url) {},
      onPageFinished: (String url) {},
      onWebResourceError: (WebResourceError error) {},
      onNavigationRequest: (NavigationRequest request) {
        if (request.url.startsWith('https://www.youtube.com/')) {
          return NavigationDecision.prevent;
        }
        return NavigationDecision.navigate;
      },
    ),
  )
  ..loadRequest(Uri.parse('https://flutter.dev')
);


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Flutter Simple Example')),
    body: WebViewWidget(controller: controller),
  );
}
参考

↑のWebView埋め込み型のところに書いてあるコードは使えないので注意(たぶん古い?).