<
コンテンツ

Android 開発者向けの Flutter

コンテンツ

このドキュメントは、Android のアプリケーションを適用しようとしている Android 開発者を対象としています。 Flutter を使用してモバイル アプリを構築するための既存の Android 知識。 Android フレームワークの基礎を理解していれば、 このドキュメントを Flutter 開発へのジャンプスタートとして使用できます。

Android を使って構築する場合、あなたの Android の知識とスキルセットは非常に価値があります。 Flutter は、多くの機能でモバイル オペレーティング システムに依存しているため、 機能と構成。 Flutter はモバイル用の UI を構築する新しい方法です。 ただし、非 UI 用に Android (および iOS) と通信するためのプラグイン システムがあります。 タスク。 Android のエキスパートであれば、すべてを学び直す必要はありません flutterを使用します。

このドキュメントは、飛び回ったり、クックブックとして使用したりできます。 自分のニーズに最も関連する質問を見つけます。

ビュー

Flutter の View に相当するものは何ですか?

Android では、Viewに表示されるすべての基礎です。 画面。ボタン、ツールバー、入力など、すべてがビューです。 Flutter では、おおよそ次のものに相当します。ViewですWidget。 ウィジェットは Android ビューに正確にマッピングされませんが、 Flutter の仕組みを理解していれば、次のように考えることができます。 「UI を宣言して構築する方法」。

ただし、これらにはいくつかの違いがあります。View。まず、ウィジェットには 異なるライフスパン: それらは不変であり、必要になるまでのみ存在します。 かわった。ウィジェットまたはその状態が変化するたびに、Flutter のフレームワークは ウィジェット インスタンスの新しいツリー。比較すると、Android ビューは 1 回描画されます。 そしてそれまで再描画しませんinvalidateと呼ばれます。

Flutter のウィジェットは、その不変性のおかげで軽量です。 それらはビューそのものではなく、直接何かを描画しているわけではないため、 むしろ、「膨張」する UI とそのセマンティクスの説明です。 ボンネットの下にある実際のビュー オブジェクトに反映されます。

flutterには以下が含まれます材料成分図書館。 これらは、マテリアル デザインのガイドライン。マテリアルデザインというのは、 柔軟な設計システムすべてのプラットフォーム向けに最適化、 iOSも含めて。

しかし、Flutter は、あらゆる設計言語を実装できる柔軟性と表現力を備えています。 たとえば、iOS では、クパチーノのウィジェット次のようなインターフェイスを生成しますAppleのiOSデザイン言語。

ウィジェットを更新するにはどうすればよいですか?

Android では、ビューを直接変更することでビューを更新します。しかし、 flutterでは、Widgetは不変であり、直接更新されません。 代わりに、ウィジェットの状態を操作する必要があります。

ここでのコンセプトは、StatefulStatelessウィジェットはから来ています。 あStatelessWidgetそれはまさにそのように聞こえます— 状態情報を持たないウィジェット。

StatelessWidgetsユーザーインターフェイスの一部である場合に便利です あなたが説明していることは、構成以外のものには依存しません オブジェクト内の情報。

たとえば、Android では、これはImageViewあなたのロゴと一緒に。実行中にロゴが変わることはありませんが、 だから、を使用してくださいStatelessWidget flutterで。

受信したデータに基づいて動的にUIを変更したい場合 HTTP 呼び出しまたはユーザー インタラクションを行った後は、作業する必要があります とStatefulWidgetFlutter フレームワークにウィジェットのStateが更新されているため、そのウィジェットを更新できるようになります。

ここで注意すべき重要なことは、ステートレスとステートフルの両方が核となるということです。 ウィジェットも同じように動作します。すべてのフレームを再構築しますが、違いは次のとおりです。StatefulWidgetがありますStateフレーム全体の状態データを保存するオブジェクト そしてそれを復元します。

迷った場合は、次のルールを常に覚えておいてください: ウィジェットが変更された場合 (ユーザーの操作などのため) ステートフルです。 ただし、ウィジェットが変更に反応する場合、それを含む親ウィジェットは、 それ自体が変化に反応しない場合は、ステートレスのままです。

次の例は、StatelessWidget。共通のStatelessWidgetそれはTextウィジェット。の実装を見てみると、 のTextウィジェットはサブクラス化されていることがわかりますStatelessWidget

Text(
  'I like Flutter!',
  style: TextStyle(fontWeight: FontWeight.bold),
);

ご覧のとおり、Textウィジェットには状態情報が関連付けられていません。 コンストラクターで渡されたものだけをレンダリングします。

しかし、「I Like Flutter」を動的に変更したい場合はどうすればよいでしょうか。 をクリックしたときの例FloatingActionButton?

これを実現するには、TextのウィジェットStatefulWidgetと ユーザーがボタンをクリックすると更新されます。

例えば:

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  // Default placeholder text.
  String textToShow = 'I Like Flutter';

  void _updateText() {
    setState(() {
      // Update the text.
      textToShow = 'Flutter is Awesome!';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: Center(child: Text(textToShow)),
      floatingActionButton: FloatingActionButton(
        onPressed: _updateText,
        tooltip: 'Update Text',
        child: const Icon(Icons.update),
      ),
    );
  }
}

ウィジェットをレイアウトするにはどうすればよいですか? XML レイアウト ファイルはどこにありますか?

Android ではレイアウトを XML で記述しますが、Flutter ではレイアウトを記述します。 ウィジェットツリー付き。

次の例は、パディング付きの単純なウィジェットを表示する方法を示しています。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Sample App'),
    ),
    body: Center(
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.only(left: 20, right: 30),
        ),
        onPressed: () {},
        child: const Text('Hello'),
      ),
    ),
  );
}

Flutter が提供するレイアウトの一部は、ウィジェットカタログ

レイアウトにコンポーネントを追加または削除するにはどうすればよいですか?

Android では、次のように呼び出します。addChild()またremoveChild()親上で子ビューを動的に追加または削除します。 Flutter では、ウィジェットは不変であるため、 直接的に相当するものはありませんaddChild()。その代わり、 ウィジェットを返す関数を親に渡すことができます。 そしてその子の作成をブールフラグで制御します。

たとえば、2 つのモードを切り替える方法は次のとおりです。 ウィジェットをクリックすると、FloatingActionButton:

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  // Default value for toggle.
  bool toggle = true;
  void _toggle() {
    setState(() {
      toggle = !toggle;
    });
  }

  Widget _getToggleChild() {
    if (toggle) {
      return const Text('Toggle One');
    } else {
      return ElevatedButton(
        onPressed: () {},
        child: const Text('Toggle Two'),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: Center(
        child: _getToggleChild(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        tooltip: 'Update Text',
        child: const Icon(Icons.update),
      ),
    );
  }
}

ウィジェットをアニメーション化するにはどうすればよいですか?

Android では、XML を使用してアニメーションを作成するか、animate()ビューのメソッド。 Flutter では、アニメーションを使用してウィジェットをアニメーション化します。 アニメーション化されたウィジェット内にウィジェットをラップすることでライブラリを作成します。

Flutter では、AnimationControllerそれはAnimation<double>アニメーションを一時停止、シーク、停止、反転できます。それには、Tickerこれは、vsync が発生したときに信号を送り、その間の線形補間を生成します。 実行中の各フレームの 0 と 1。次に、1 つ以上を作成しますAnimations をコントローラーに取り付けます。

たとえば、次のように使用できます。CurvedAnimationアニメーションを実装するには 補間された曲線に沿って。そういう意味ではコントローラーは はアニメーションの進行状況の「マスター」ソースであり、CurvedAnimationコントローラのデフォルトの直線運動を置き換える曲線を計算します。 ウィジェットと同様に、Flutter のアニメーションは合成で動作します。

ウィジェット ツリーを構築するときに、Animationアニメーションに ウィジェットの不透明度などのプロパティFadeTransition、と伝えます。 コントローラーを押してアニメーションを開始します。

次の例は、FadeTransitionそれは色褪せます を押すとウィジェットがロゴに組み込まれますFloatingActionButton:

import 'package:flutter/material.dart';

void main() {
  runApp(const FadeAppTest());
}

class FadeAppTest extends StatelessWidget {
  const FadeAppTest({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fade Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyFadeTest(title: 'Fade Demo'),
    );
  }
}

class MyFadeTest extends StatefulWidget {
  const MyFadeTest({super.key, required this.title});

  final String title;
  @override
  State<MyFadeTest> createState() => _MyFadeTest();
}

class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
  late AnimationController controller;
  late CurvedAnimation curve;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(milliseconds: 2000),
      vsync: this,
    );
    curve = CurvedAnimation(
      parent: controller,
      curve: Curves.easeIn,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: FadeTransition(
          opacity: curve,
          child: const FlutterLogo(
            size: 100,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        tooltip: 'Fade',
        onPressed: () {
          controller.forward();
        },
        child: const Icon(Icons.brush),
      ),
    );
  }
}

詳細については、を参照してください。アニメーションとモーションのウィジェット、 のアニメーションのチュートリアル、 そしてそのアニメーションの概要

Canvas を使用して描画/ペイントするにはどうすればよいですか?

Android では、CanvasDrawable画像や図形を画面に描画します。 Flutterにも同様のものがありますCanvasAPIもそうですが、 同じ低レベルのレンダリング エンジンである Skia に基づいているためです。 その結果、Flutter でキャンバスにペイントする Android 開発者にとっては非常に馴染みのあるタスクです。

Flutter には、キャンバスへの描画に役立つ 2 つのクラスがあります。CustomPaintCustomPainter、 後者は、キャンバスに描画するアルゴリズムを実装します。

Flutter で署名ペインタを実装する方法を学ぶには、 コリンの答えを参照してくださいカスタムペイント。

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: DemoApp()));

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) => const Scaffold(body: Signature());
}

class Signature extends StatefulWidget {
  const Signature({super.key});

  @override
  SignatureState createState() => SignatureState();
}

class SignatureState extends State<Signature> {
  List<Offset?> _points = <Offset>[];
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          RenderBox? referenceBox = context.findRenderObject() as RenderBox;
          Offset localPosition =
              referenceBox.globalToLocal(details.globalPosition);
          _points = List.from(_points)..add(localPosition);
        });
      },
      onPanEnd: (details) => _points.add(null),
      child: CustomPaint(
        painter: SignaturePainter(_points),
        size: Size.infinite,
      ),
    );
  }
}

class SignaturePainter extends CustomPainter {
  SignaturePainter(this.points);
  final List<Offset?> points;
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..color = Colors.black
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5;
    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null) {
        canvas.drawLine(points[i]!, points[i + 1]!, paint);
      }
    }
  }

  @override
  bool shouldRepaint(SignaturePainter oldDelegate) =>
      oldDelegate.points != points;
}

カスタム ウィジェットを作成するにはどうすればよいですか?

Android では通常、サブクラス化します。View、または既存のビューを使用します。 目的の動作を実現するメソッドをオーバーライドして実装します。

Flutter では、次のようにカスタム ウィジェットを構築します。作曲する(ウィジェットを拡張する代わりに) より小さなウィジェットを作成します。 カスタムの実装に似ていますViewGroupAndroid では、すべての構成要素がすでに存在しています。 ただし、別の動作を提供します。たとえば、 カスタム レイアウト ロジック。

たとえば、どうやって構築しますかCustomButtonそれはラベルを取り込みます コンストラクターは?を構成する CustomButton を作成します。ElevatedButtonと ラベルを拡張するのではなく、ElevatedButton:

class CustomButton extends StatelessWidget {
  final String label;

  const CustomButton(this.label, {super.key});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {},
      child: Text(label),
    );
  }
}

次に、使用しますCustomButton他の Flutter ウィジェットを使用するのと同じように、

@override
Widget build(BuildContext context) {
  return const Center(
    child: CustomButton('Hello'),
  );
}

インテント

Flutter のインテントに相当するものは何ですか?

Android には、主に 2 つの使用例があります。Intents: 間を移動します アクティビティ、およびコンポーネントとの通信。一方、 flutterは、 インテントの概念はありませんが、インテントを開始することはできます。 ネイティブ統合を通じて (使用プラグイン)。

Flutter にはアクティビティやフラグメントに直接相当するものはありません。 むしろ、Flutter では、NavigatorRouteすべて同じ内にありますActivity

Routeアプリの「画面」または「ページ」の抽象化です。Navigatorルートを管理するウィジェットです。ルートはおおよそ次のようにマッピングされます。Activity, しかし、同じ意味ではありません。ナビゲーターがプッシュできる およびポップルートを使用して画面から画面に移動します。ナビゲーターはスタックのように機能します どれでできるかpush()移動先または出発地となる新しいルート あなたができることpop()「戻りたい」ときのルート。

Android では、アプリ内でアクティビティを宣言します。AndroidManifest.xml

Flutter では、ページ間を移動するためのオプションがいくつかあります。

  • を指定してくださいMap路線名のこと。 (使用してMaterialApp)
  • ルートに直接移動します。 (使用してWidgetsApp)

次の例ではマップを構築します。

void main() {
  runApp(MaterialApp(
    home: const MyAppHome(), // Becomes the route named '/'.
    routes: <String, WidgetBuilder>{
      '/a': (context) => const MyPage(title: 'page A'),
      '/b': (context) => const MyPage(title: 'page B'),
      '/c': (context) => const MyPage(title: 'page C'),
    },
  ));
}

ルートに移動するにはpushその名前をNavigator

Navigator.of(context).pushNamed('/b');

もう 1 つの一般的なユースケースは、Intents は、次のような外部コンポーネントを呼び出すことです。 カメラまたはファイルピッカーとして。このためには、ネイティブ プラットフォームを作成する必要があります。 統合 (または既存のプラグイン)。

ネイティブ プラットフォーム統合を構築する方法を学ぶには、 見るパッケージとプラグインの開発

Flutter で外部アプリケーションから受信したインテントをどのように処理すればよいですか?

Flutter は、Android からの受信インテントを直接処理することができます。 Android レイヤーと共有されたデータをリクエストします。

次の例では、テキスト共有インテント フィルターをネイティブに登録します。 Flutter コードを実行するアクティビティ。これにより、他のアプリがテキストを共有できるようになります。 Flutter アプリ。

基本的なフローは、最初に共有テキスト データを Android ネイティブ側 (私たちのActivity)、Flutter がリクエストするまで待ちます データを使用して提供するには、MethodChannel

まず、すべてのインテントのインテント フィルターをAndroidManifest.xml:

<activity
  android:name=".MainActivity"
  android:launchMode="singleTop"
  android:theme="@style/LaunchTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize">
  <!-- ... -->
  <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
  </intent-filter>
</activity>

それからMainActivity、インテントを処理し、含まれていたテキストを抽出します。 意図から共有し、それを保持します。 Flutter が処理できる状態になったら、 プラットフォーム チャネルを使用してデータをリクエストし、送信されます ネイティブ側の向かい側:

package com.example.shared;

import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.NonNull;

import io.flutter.plugin.common.MethodChannel;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {

  private String sharedText;
  private static final String CHANNEL = "app.channel.shared.data";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
      if ("text/plain".equals(type)) {
        handleSendText(intent); // Handle text being sent
      }
    }
  }

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
      GeneratedPluginRegistrant.registerWith(flutterEngine);

      new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
              .setMethodCallHandler(
                      (call, result) -> {
                          if (call.method.contentEquals("getSharedText")) {
                              result.success(sharedText);
                              sharedText = null;
                          }
                      }
              );
  }

  void handleSendText(Intent intent) {
    sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
  }
}

最後にFlutter側にデータをリクエストします。 ウィジェットがレンダリングされるとき:

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

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample Shared App Handler',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  static const platform = MethodChannel('app.channel.shared.data');
  String dataShared = 'No data';

  @override
  void initState() {
    super.initState();
    getSharedText();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(child: Text(dataShared)));
  }

  Future<void> getSharedText() async {
    var sharedData = await platform.invokeMethod('getSharedText');
    if (sharedData != null) {
      setState(() {
        dataShared = sharedData;
      });
    }
  }
}

startActivityForResult() に相当するものは何ですか?

Navigatorクラスは Flutter でルーティングを処理し、取得するために使用されます。 スタックにプッシュしたルートから返された結果。 これを行うのは、await上にいるFutureによって返されましたpush()

たとえば、ユーザーが選択できる位置ルートを開始するには、 その位置を確認するには、次のことができます。

Object? coordinates = await Navigator.of(context).pushNamed('/location');

そして、ユーザーが自分の場所を選択すると、場所のルート内で あなたはできるpop結果を含むスタック:

Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});

非同期UI

Flutter の runOnUiThread() に相当するものは何ですか?

Dart にはシングルスレッド実行モデルがあり、Isolates (別のスレッドで Dart コードを実行する方法)、イベント ループ、および 非同期プログラミング。あなたがスポーンしない限り、Isolate、ダーツコード メイン UI スレッドで実行され、イベント ループによって駆動されます。 flutterのイベント ループはAndroidのメインに相当しますLooper—つまり、Looperそれか メインスレッドに接続されています。

Dart のシングルスレッド モデルは、すべてを 1 つのスレッドとして実行する必要があるという意味ではありません。 UI のフリーズを引き起こす操作をブロックします。 Android とは異なり、 Flutter では、メインスレッドを常にフリーにしておく必要があります。 Dart 言語が提供する非同期機能を使用します。async/await、非同期作業を実行します。あなたもよく知っているかもしれません のasync/awaitC#、JavaScript でパラダイムを使用したことがある場合、または Kotlin のコルーチンを使用しました。

たとえば、UI をハングさせることなくネットワーク コードを実行できます。 使用してasync/awaitそして、Dart に面倒な作業を任せます。

Future<void> loadData() async {
  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  http.Response response = await http.get(dataURL);
  setState(() {
    widgets = jsonDecode(response.body);
  });
}

一度awaitネットワーク呼び出しが完了しました。呼び出して UI を更新しますsetState()、 これにより、ウィジェットのサブツリーの再構築がトリガーされ、データが更新されます。

次の例では、データを非同期的にロードし、それをListView:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: ListView.builder(
        itemCount: widgets.length,
        itemBuilder: (context, position) {
          return getRow(position);
        },
      ),
    );
  }

  Widget getRow(int i) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text("Row ${widgets[i]["title"]}"),
    );
  }

  Future<void> loadData() async {
    var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
    http.Response response = await http.get(dataURL);
    setState(() {
      widgets = jsonDecode(response.body);
    });
  }
}

での作業の詳細については、次のセクションを参照してください。 背景、および Flutter と Android の違いについて説明します。

作業をバックグラウンド スレッドに移動するにはどうすればよいでしょうか?

Android では、ネットワーク リソースにアクセスする場合、通常は次のようにします。 メインスレッドをブロックしないように、バックグラウンドスレッドに移動して作業を実行します。 そしてANRを避けてください。たとえば、次のようなものを使用している可能性があります。AsyncTaskLiveData、 のIntentServiceJobSchedulerジョブ、または RxJava パイプライン バックグラウンドスレッドで動作するスケジューラ。

Flutter はシングルスレッドでイベント ループ (Node.js など) を実行するため、 スレッド管理やバックグラウンド スレッドの生成について心配する必要はありません。もしも ディスク アクセスやネットワーク呼び出しなど、I/O バウンドの作業を行っている場合 安全に使用できますasync/awaitこれで準備は完了です。一方、もし、 一方、CPU をビジー状態にし続ける計算集約的な作業を行う必要があるため、 それをに移動したいのですが、Isolateイベントループのブロックを避けるため、次のように あなたは保管するでしょうどれでもAndroid のメインスレッドから外れた一種の作業です。

I/O バウンドの作業の場合は、関数を次のように宣言します。async関数、 とawait関数内の長時間実行タスクの場合:

Future<void> loadData() async {
  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  http.Response response = await http.get(dataURL);
  setState(() {
    widgets = jsonDecode(response.body);
  });
}

これは、通常、ネットワーク呼び出しまたはデータベース呼び出しを行う方法です。 I/O 操作。

Android で拡張すると、AsyncTask通常、3 つのメソッドをオーバーライドします。onPreExecute()doInBackground()onPostExecute()。ありません Flutterでも同等です。await長時間実行される関数について、そして Dart のイベント ループが残りを処理します。

ただし、大量のデータを処理する場合や、 UI がハングします。 Flutterでは、使用しますIsolateを活用する 複数の CPU コアを使用して、長時間実行または計算負荷の高いタスクを実行します。

アイソレートはメモリを共有しない別個の実行スレッドです。 メイン実行メモリヒープを使用します。これは、から変数にアクセスできないことを意味します メインスレッドを呼び出すか、呼び出して UI を更新しますsetState()。 Android のスレッドとは異なり、 アイソレートはその名の通り、記憶を共有できない (たとえば、静的フィールドの形式で)。

次の例は、単純な分離でデータを共有する方法を示しています。 UIを更新するメインスレッド。

Future<void> loadData() async {
  ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(dataLoader, receivePort.sendPort);

  // The 'echo' isolate sends its SendPort as the first message.
  SendPort sendPort = await receivePort.first;

  List msg = await sendReceive(
    sendPort,
    'https://jsonplaceholder.typicode.com/posts',
  );

  setState(() {
    widgets = msg;
  });
}

// The entry point for the isolate.
static Future<void> dataLoader(SendPort sendPort) async {
  // Open the ReceivePort for incoming messages.
  ReceivePort port = ReceivePort();

  // Notify any other isolates what port this isolate listens to.
  sendPort.send(port.sendPort);

  await for (var msg in port) {
    String data = msg[0];
    SendPort replyTo = msg[1];

    String dataURL = data;
    http.Response response = await http.get(Uri.parse(dataURL));
    // Lots of JSON to parse
    replyTo.send(jsonDecode(response.body));
  }
}

Future sendReceive(SendPort port, msg) {
  ReceivePort response = ReceivePort();
  port.send([msg, response.sendPort]);
  return response.first;
}

ここ、dataLoader()それはIsolate独自の個別で実行される 実行スレッド。分離では、より多くの CPU を使用する実行が可能になります 処理 (大きな JSON の解析など)、 または、計算量の多い数学を実行する場合、 暗号化や信号処理など。

以下の完全な例を実行できます。

import 'dart:async';
import 'dart:convert';
import 'dart:isolate';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  Widget getBody() {
    bool showLoadingDialog = widgets.isEmpty;
    if (showLoadingDialog) {
      return getProgressDialog();
    } else {
      return getListView();
    }
  }

  Widget getProgressDialog() {
    return const Center(child: CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: getBody(),
    );
  }

  ListView getListView() {
    return ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (context, position) {
        return getRow(position);
      },
    );
  }

  Widget getRow(int i) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text("Row ${widgets[i]["title"]}"),
    );
  }

  Future<void> loadData() async {
    ReceivePort receivePort = ReceivePort();
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    // The 'echo' isolate sends its SendPort as the first message.
    SendPort sendPort = await receivePort.first;

    List msg = await sendReceive(
      sendPort,
      'https://jsonplaceholder.typicode.com/posts',
    );

    setState(() {
      widgets = msg;
    });
  }

  // The entry point for the isolate.
  static Future<void> dataLoader(SendPort sendPort) async {
    // Open the ReceivePort for incoming messages.
    ReceivePort port = ReceivePort();

    // Notify any other isolates what port this isolate listens to.
    sendPort.send(port.sendPort);

    await for (var msg in port) {
      String data = msg[0];
      SendPort replyTo = msg[1];

      String dataURL = data;
      http.Response response = await http.get(Uri.parse(dataURL));
      // Lots of JSON to parse
      replyTo.send(jsonDecode(response.body));
    }
  }

  Future sendReceive(SendPort port, msg) {
    ReceivePort response = ReceivePort();
    port.send([msg, response.sendPort]);
    return response.first;
  }
}

Flutter の OkHttp に相当するものは何ですか?

Flutter でネットワーク呼び出しを行うのは、 人気httpパッケージ。

http パッケージには OkHttp にあるすべての機能が含まれているわけではありませんが、 通常実装するネットワーキングの多くを抽象化します。 ネットワーク通話を簡単に行うことができます。

追加するには、httpパッケージを依存関係として実行しますflutter pub add:

$ flutter pub add http

ネットワーク通話を行うには、awaitasync関数http.get():

import 'dart:developer' as developer;
import 'package:http/http.dart' as http;

Future<void> loadData() async {
  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  http.Response response = await http.get(dataURL);
  developer.log(response.body);
}

長時間実行されるタスクの進行状況を表示するにはどうすればよいですか?

Android では通常、ProgressBarUI で表示しながら、 バックグラウンド スレッドで長時間実行されるタスクを実行する。

Flutter では、ProgressIndicatorウィジェット。 レンダリングのタイミングを制御することで、プログラムで進行状況を表示します ブール値フラグを介して。 Flutter に、実行前に状態を更新するように指示します。 長時間実行タスクが開始され、終了後に非表示になります。

次の例では、ビルド関数が 3 つの異なる関数に分割されています。 機能。もしもshowLoadingDialogtrue(いつwidgets.isEmpty)、 次にレンダリングしますProgressIndicator。それ以外の場合は、レンダリングListViewネットワーク呼び出しから返されたデータを使用します。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  Widget getBody() {
    bool showLoadingDialog = widgets.isEmpty;
    if (showLoadingDialog) {
      return getProgressDialog();
    } else {
      return getListView();
    }
  }

  Widget getProgressDialog() {
    return const Center(child: CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: getBody(),
    );
  }

  ListView getListView() {
    return ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (context, position) {
        return getRow(position);
      },
    );
  }

  Widget getRow(int i) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text("Row ${widgets[i]["title"]}"),
    );
  }

  Future<void> loadData() async {
    var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
    http.Response response = await http.get(dataURL);
    setState(() {
      widgets = jsonDecode(response.body);
    });
  }
}

プロジェクトの構造とリソース

解像度に依存する画像ファイルはどこに保存すればよいですか?

Android はリソースとアセットを別個のアイテムとして扱いますが、 Flutter アプリにはアセットのみがあります。生存するすべてのリソース の中にres/drawable-*Android 上のフォルダー、 Flutter のアセットフォルダーに配置されます。

Flutter は、iOS のような単純な密度ベースの形式に従います。 資産は次のとおりである可能性があります1.0x2.0x3.0x、またはその他の乗数。 flutterにはありませんdp論理ピクセルはありますが、 これは基本的にデバイスに依存しないピクセルと同じです。 flutterズdevicePixelRatio比率を表します 単一の論理ピクセル内の物理ピクセルの数。

Android の密度バケットに相当するものは次のとおりです。

Android 密度修飾子 flutterピクセル率
ldpi 0.75x
mdpi 1.0x
hdpi 1.5x
xhdpi 2.0x
xxhdpi 3.0x
xxxhdpi 4.0x

アセットは任意のフォルダー (Flutter) にあります。 事前定義されたフォルダー構造はありません。 資産を(場所とともに)宣言します。 のpubspec.yamlファイルを開くと、Flutter がそれらを取得します。

ネイティブ アセット フォルダーに保存されているアセットは、 Android を使用してネイティブ側でアクセスするAssetManager:

val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png")

Flutter はネイティブのリソースやアセットにアクセスできません。

という新しい画像アセットを追加するには、my_icon.pngFlutter プロジェクトに、 たとえば、フォルダーに保存する必要があると判断した場合、 勝手に呼ばれるimages、ベース画像 (1.0x) を配置します。 の中にimagesフォルダー、およびサブフォルダー内の他のすべてのバリアント 適切な比率乗数を使用して呼び出されます。

images/my_icon.png       // Base: 1.0x image
images/2.0x/my_icon.png  // 2.0x image
images/3.0x/my_icon.png  // 3.0x image

次に、これらのイメージをpubspec.yamlファイル:

assets:
 - images/my_icon.jpeg

その後、次を使用して画像にアクセスできますAssetImage:

AssetImage('images/my_icon.jpeg')

または直接Imageウィジェット:

@override
Widget build(BuildContext context) {
  return Image.asset('images/my_image.png');
}

文字列はどこに保存すればよいですか?ローカリゼーションはどのように処理すればよいですか?

Flutter には現在、文字列用の専用のリソースのようなシステムがありません。 現時点でのベスト プラクティスは、コピー テキストをクラス内に保持することです。 静的フィールドとそこからのアクセス。例えば:

class Strings {
  static String welcomeMessage = 'Welcome To Flutter';
}

次に、コード内で次のように文字列にアクセスできます。

Text(Strings.welcomeMessage);

Flutter は Android でのアクセシビリティの基本的なサポートを備えています。 ただし、この機能は開発中のものです。

Flutter 開発者は、国際パッケージ国際化とローカリゼーションのために。

Gradle ファイルに相当するものは何ですか?依存関係を追加するにはどうすればよいですか?

Android では、Gradle ビルド スクリプトに追加することで依存関係を追加します。 Flutter は、Dart 独自のビルド システムと Pub パッケージ マネージャーを使用します。 このツールは、ネイティブ Android および iOS の構築を委任します。 ラッパー アプリをそれぞれのビルド システムに追加します。

Gradle ファイルは以下にありますが、androidフォルダー内の Flutter プロジェクト。ネイティブを追加する場合にのみこれらを使用します。 プラットフォームごとの統合に必要な依存関係。 一般に、使用しますpubspec.yaml宣言する Flutter で使用する外部依存関係。 Flutter パッケージを見つけるのに適した場所は次のとおりです。パブ.dev。

アクティビティとフラグメント

Flutter のアクティビティとフラグメントに相当するものは何ですか?

Android では、Activityユーザーが実行できる単一の焦点を絞ったことを表します。 あFragment動作またはユーザー インターフェイスの一部を表します。 フラグメントはコードをモジュール化し、洗練されたコードを構成する方法です。 より大きな画面向けのユーザー インターフェイスを提供し、アプリケーション UI の拡張に役立ちます。 Flutter では、これらの概念は両方とも以下の範疇に当てはまります。Widgets.

アクティビティとフラグメントを構築するための UI について詳しくは、 コミュニティが投稿した Medium の記事を参照してください。Android 開発者向け Flutter: Flutter でアクティビティ UI を設計する方法。

で述べたように、インテントセクション、 Flutter の画面は次のように表されます。Widgetすべてがそうだから Flutter のウィジェット。使うNavigator異なる間を移動するRouteさまざまな画面またはページを表す あるいは、同じデータの異なる状態やレンダリングが考えられます。

Android アクティビティのライフサイクル イベントをリッスンするにはどうすればよいですか?

Android では、Activityライフサイクルを捉える アクティビティ自体のメソッド、または登録ActivityLifecycleCallbacksの上 のApplication。 Flutter にはどちらの概念もありませんが、代わりに次のことができます。 にフックしてライフサイクル イベントをリッスンします。WidgetsBinding観察者と を聞いているdidChangeAppLifecycleState()変更イベント。

監視可能なライフサイクル イベントは次のとおりです。

  • e4029409-2530-41b7-9dc7-f78aecbba26— アプリケーションは依然として flutter エンジン上でホストされていますが、ホスト ビューからは切り離されています。
  • inactive— アプリケーションは非アクティブ状態にあり、ユーザーを受信して​​いません。 入力。
  • paused— アプリケーションは現在ユーザーに表示されていません。 ユーザー入力に応答せず、バックグラウンドで実行されます。 これは以下と同等ですonPause()アンドロイドで。
  • resumed— アプリケーションが表示され、ユーザー入力に応答します。 これは以下と同等ですonPostResume()アンドロイドで。

これらの状態の意味の詳細については、「AppLifecycleStatusドキュメンテーション。

お気づきかと思いますが、アクティビティのほんの一部だけが ライフサイクル イベントが利用可能です。その間FlutterActivityする ほぼすべてのアクティビティ ライフサイクル イベントを内部的にキャプチャし、 それらを Flutter エンジンに送信します。ほとんどがシールドされています あなたから離れて。 Flutter は、 あなたのためのエンジンであり、そうする必要がある理由はほとんどありません ほとんどの場合、Flutter 側でアクティビティのライフサイクルを観察します。 ライフサイクルを観察して取得または解放する必要がある場合は、 ネイティブ リソースの場合、おそらくネイティブ側から実行する必要があります。 いずれにせよ。

以下は、ライフサイクルステータスを観察する方法の例です。 アクティビティを含む:

import 'package:flutter/widgets.dart';

class LifecycleWatcher extends StatefulWidget {
  const LifecycleWatcher({super.key});

  @override
  State<LifecycleWatcher> createState() => _LifecycleWatcherState();
}

class _LifecycleWatcherState extends State<LifecycleWatcher>
    with WidgetsBindingObserver {
  AppLifecycleState? _lastLifecycleState;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(() {
      _lastLifecycleState = state;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_lastLifecycleState == null) {
      return const Text(
        'This widget has not observed any lifecycle changes.',
        textDirection: TextDirection.ltr,
      );
    }

    return Text(
      'The most recent lifecycle state this widget observed was: $_lastLifecycleState.',
      textDirection: TextDirection.ltr,
    );
  }
}

void main() {
  runApp(const Center(child: LifecycleWatcher()));
}

レイアウト

LinearLayout に相当するものは何ですか?

Android では、ウィジェットをレイアウトするために LinearLayout が使用されます 水平方向または垂直方向に直線的に。 Flutter では、行または列を使用します ウィジェットを使用して同じ結果を実現します。

2 つのコード サンプルが次の点を除いて同一であることに気付いた場合は、 「行」と「列」ウィジェット。子は同じなので、この機能は次のようになります。 時間の経過とともに変更できるリッチなレイアウトの開発に利用されます。 子供。

@override
Widget build(BuildContext context) {
  return const Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Text('Row One'),
      Text('Row Two'),
      Text('Row Three'),
      Text('Row Four'),
    ],
  );
}
@override
Widget build(BuildContext context) {
  return const Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Text('Column One'),
      Text('Column Two'),
      Text('Column Three'),
      Text('Column Four'),
    ],
  );
}

線形レイアウトの構築について詳しくは、 コミュニティが投稿した Medium 記事を参照してくださいAndroid 開発者向け Flutter: Flutter で LinearLayout を設計する方法。

RelativeLayout に相当するものは何ですか?

RelativeLayout は、ウィジェットを相互に相対的に配置します。の Flutter では、同じ結果を達成する方法がいくつかあります。

次の組み合わせを使用して、RelativeLayout の結果を実現できます。 列、行、およびスタックのウィジェット。ウィジェットのルールを指定できます 親に対して子がどのように配置されるかを指定するコンストラクター。

Flutter で RelativeLayout を構築する良い例として、 コリンの答えを参照してくださいスタックオーバーフロー。

ScrollView に相当するものは何ですか?

Android では、ScrollView を使用してウィジェットをレイアウトします。 デバイスの画面がコンテンツより小さい場合、スクロールします。

Flutter では、これを行う最も簡単な方法は ListView ウィジェットを使用することです。 これは Android のやりすぎのように思えるかもしれませんが、 しかし、Flutter では ListView ウィジェットは ScrollView と Android ListView の両方。

@override
Widget build(BuildContext context) {
  return ListView(
    children: const <Widget>[
      Text('Row One'),
      Text('Row Two'),
      Text('Row Three'),
      Text('Row Four'),
    ],
  );
}

Flutter でランドスケープの遷移を処理するにはどうすればよいですか?

AndroidManifest.xml に以下が含まれている場合、FlutterView は構成変更を処理します。

android:configChanges="orientation|screenSize"

ジェスチャー検出とタッチイベント処理

Flutter のウィジェットに onClick リスナーを追加するにはどうすればよいですか?

Android では、呼び出して onClick をボタンなどのビューにアタッチできます。 メソッド「setOnClickListener」。

Flutter では、タッチ リスナーを追加する方法が 2 つあります。

  1. ウィジェットがイベント検出をサポートしている場合は、ウィジェットに関数を渡して処理します。 関数内で。たとえば、ElevatedButton にはonPressedパラメータ:
@override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        developer.log('click');
      },
      child: const Text('Button'),
    );
  }
  1. ウィジェットがイベント検出をサポートしていない場合は、 GestureDetector のウィジェットを作成し、関数をonTapパラメータ。
class SampleTapApp extends StatelessWidget {
    const SampleTapApp({super.key});

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: Center(
          child: GestureDetector(
            onTap: () {
              developer.log('tap');
            },
            child: const FlutterLogo(
              size: 200,
            ),
          ),
        ),
      );
    }
  }

ウィジェット上で他のジェスチャを処理するにはどうすればよいですか?

GestureDetector を使用すると、次のような幅広いジェスチャを聞くことができます。

  • タップ

    • onTapDown- タップの原因となるポインタが画面に接触した場合 特定の場所。
    • onTapUp- タップをトリガーするポインタが接触を停止しました。 特定の場所の画面。
    • onTap- タップが発生しました。
    • onTapCancel- 以前にトリガーしたポインターonTapDownしません タップの原因となります。
  • ダブルタップ

    • onDoubleTap- ユーザーが画面の同じ場所を 2 回タップしました。 素早い連続。
  • 長押し

    • onLongPress- ポインタが画面に接触したままになっている 長期間同じ場所にいること。
  • 垂直方向のドラッグ

    • onVerticalDragStart- ポインタが画面に触れて、 垂直方向に動き始めるかもしれません。
    • onVerticalDragUpdate- 画面に接触しているポインタ さらに垂直方向に移動しました。
    • onVerticalDragEnd- 以前に接触していたポインタ 画面と垂直に移動すると画面と接触しなくなり、 画面との接触が停止すると、特定の速度で移動します。
  • 水平方向のドラッグ

    • onHorizontalDragStart- ポインタが画面に接触し、開始する可能性があります。 水平方向に移動します。
    • onHorizontalDragUpdate- 画面に接触しているポインタ さらに水平方向に移動しました。
    • onHorizontalDragEnd- 以前に接触していたポインタ 画面と水平方向の移動が接触しなくなりました。 画面が表示され、停止したときに特定の速度で移動していました 画面に接触しています。

次の例は、GestureDetectorダブルタップで Flutter ロゴを回転させます。

class SampleApp extends StatefulWidget {
  const SampleApp({super.key});

  @override
  State<SampleApp> createState() => _SampleAppState();
}

class _SampleAppState extends State<SampleApp>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late CurvedAnimation curve;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 2000),
    );
    curve = CurvedAnimation(
      parent: controller,
      curve: Curves.easeIn,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onDoubleTap: () {
            if (controller.isCompleted) {
              controller.reverse();
            } else {
              controller.forward();
            }
          },
          child: RotationTransition(
            turns: curve,
            child: const FlutterLogo(
              size: 200,
            ),
          ),
        ),
      ),
    );
  }
}

リストビューとアダプター

Flutter の ListView の代替となるものは何ですか?

Flutter の ListView に相当するのは、ListView です。

Android ListView では、アダプターを作成し、それを ListView。アダプターが返す内容を使用して各行をレンダリングします。しかし、あなたは 行を必ずリサイクルする必要があります。そうしないと、あらゆる種類のおかしなことになります 視覚的な不具合とメモリの問題。

Flutter の不変ウィジェット パターンにより、次のリストを渡します。 ウィジェットを ListView に追加すると、Flutter がそれを確認します。 スクロールが速くてスムーズです。

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: ListView(children: _getListData()),
    );
  }

  List<Widget> _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $i'),
      ));
    }
    return widgets;
  }
}

どのリスト項目がクリックされたかを確認するにはどうすればよいですか?

Android では、ListView にどの項目がクリックされたかを調べるメソッドがあります。 「onItemClickListener」。 Flutter では、渡されたウィジェットによって提供されるタッチ処理を使用します。

import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: ListView(children: _getListData()),
    );
  }

  List<Widget> _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(
        GestureDetector(
          onTap: () {
            developer.log('row tapped');
          },
          child: Padding(
            padding: const EdgeInsets.all(10),
            child: Text('Row $i'),
          ),
        ),
      );
    }
    return widgets;
  }
}

ListView を動的に更新するにはどうすればよいですか?

Android では、アダプターを更新して呼び出します。notifyDataSetChanged

Flutter で、ウィジェット内のウィジェットのリストを更新するとします。setState()、 データが視覚的に変化していないことがすぐにわかります。 なぜなら、いつsetState()Flutter レンダリング エンジンと呼ばれます ウィジェット ツリーを見て、何か変更があったかどうかを確認します。それがあなたの手元に届いたら、ListViewを実行します。==チェックして、その 2 つが一致していると判断します。ListViewも同じです。何も変更されていないため、更新する必要はありません。

を更新する簡単な方法については、ListView、 新しいを作成しますListの中にsetState()をクリックして、古いリストから新しいリストにデータをコピーします。 このアプローチはシンプルですが、大規模なデータセットには推奨されません。 次の例に示すように。

import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: ListView(children: widgets),
    );
  }

  Widget getRow(int i) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets = List.from(widgets);
          widgets.add(getRow(widgets.length));
          developer.log('row $i');
        });
      },
      child: Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $i'),
      ),
    );
  }
}

リストを作成するための推奨される、効率的かつ効果的な方法では、ListView.Builder。この方法は、動的なイベントがある場合に最適です。ListまたはList非常に大量のデータが含まれます。これは本質的には Android の RecyclerView に相当し、自動的に リスト要素をリサイクルします。

import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: ListView.builder(
        itemCount: widgets.length,
        itemBuilder: (context, position) {
          return getRow(position);
        },
      ),
    );
  }

  Widget getRow(int i) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets.add(getRow(widgets.length));
          developer.log('row $i');
        });
      },
      child: Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $i'),
      ),
    );
  }
}

「ListView」を作成する代わりに、ListView.builderこれは 2 つの重要なパラメータを取ります。 リストの初期の長さ、およびItemBuilder関数。

ItemBuilder関数は次のようなものですgetViewAndroid アダプターで機能します。それは位置をとり、 そして、その位置にレンダリングしたい行を返します。

最後に、しかし最も重要なことですが、onTap()関数 リストはもう再作成されませんが、代わりに.addそれには。

テキストの操作

テキスト ウィジェットにカスタム フォントを設定するにはどうすればよいですか?

Android SDK (Android O 以降) では、フォント リソース ファイルを作成し、 それを TextView の FontFamily パラメータに渡します。

Flutter では、フォント ファイルをフォルダーに配置し、pubspec.yaml画像をインポートする方法と同様に、ファイルを作成します。

fonts:
   - family: MyCustomFont
     fonts:
       - asset: fonts/MyCustomFont.ttf
       - style: italic

次にフォントをTextウィジェット:

a44f6b88-16bd-4d7d-ベッド9-3c25164122d5

テキスト ウィジェットのスタイルを設定するにはどうすればよいですか?

フォントに加えて、他のスタイル要素もカスタマイズできます。Textウィジェット。 のスタイルパラメータTextウィジェットはTextStyleできる限りオブジェクトを 次のような多くのパラメータをカスタマイズします。

  • 装飾
  • 装飾色
  • 装飾スタイル
  • フォントファミリー
  • フォントサイズ
  • フォントスタイル
  • フォントの太さ
  • ハッシュコード
  • 身長
  • 継承する
  • 文字間隔
  • テキストベースライン
  • 単語間隔

フォーム入力

フォームの使用方法の詳細については、 見るテキストフィールドの値を取得する、 から flutterクックブック

入力における「ヒント」に相当するものは何ですか?

Flutter では、次の方法で入力の「ヒント」またはプレースホルダー テキストを簡単に表示できます。 InputDecoration オブジェクトを装飾コンストラクター パラメーターに追加します。 テキストウィジェット。

Center(
  child: TextField(
    decoration: InputDecoration(hintText: 'This is a hint'),
  ),
)

検証エラーを表示するにはどうすればよいですか?

「ヒント」と同じように、InputDecoration オブジェクトを渡します。 Text ウィジェットの装飾コンストラクターに追加します。

ただし、最初からエラーを表示することは望ましくありません。 代わりに、ユーザーが無効なデータを入力した場合、 状態を更新し、新しい値を渡しますInputDecoration物体。

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  String? _errorText;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample App'),
      ),
      body: Center(
        child: TextField(
          onSubmitted: (text) {
            setState(() {
              if (!isEmail(text)) {
                _errorText = 'Error: This is not an email';
              } else {
                _errorText = null;
              }
            });
          },
          decoration: InputDecoration(
            hintText: 'This is a hint',
            errorText: _getErrorText(),
          ),
        ),
      ),
    );
  }

  String? _getErrorText() {
    return _errorText;
  }

  bool isEmail(String em) {
    String emailRegexp =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'
        r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'
        r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

    RegExp regExp = RegExp(emailRegexp);

    return regExp.hasMatch(em);
  }
}

flutterプラグイン

GPS センサーにアクセスするにはどうすればよいですか?

使用geolocatorコミュニティプラグイン。

カメラにアクセスするにはどうすればよいですか?

image_pickerプラグインが人気です カメラにアクセスするため。

Facebook にログインするにはどうすればよいですか?

Facebook でログインするには、flutter_facebook_loginコミュニティプラグイン。

Firebase の機能を使用するにはどうすればよいですか?

Firebase のほとんどの機能は以下でカバーされています。ファーストパーティのプラグイン。 これらのプラグインはファーストパーティの統合です。 Flutter チームによって保守されています。

  • firebase_admobFirebase AdMob 用
  • firebase_analyticsFirebase アナリティクス用
  • firebase_authFirebase認証の場合
  • firebase_databaseFirebase RTDB 用
  • firebase_storageFirebaseクラウドストレージ用
  • firebase_messagingFirebase メッセージング (FCM) 用
  • flutter_firebase_uiFirebase Auth 統合を迅速に行うため (Facebook、Google、Twitter、電子メール)
  • cloud_firestoreFirebase クラウド Firestore 用

サードパーティの Firebase プラグインも次のサイトで見つけることができます。 pub.dev は、直接カバーされていない領域をカバーします。 ファーストパーティのプラグイン。

独自のカスタム ネイティブ統合を構築するにはどうすればよいですか?

Flutter が実行するプラットフォーム固有の機能がある場合 またはそのコミュニティのプラグインが見つからない、 次のように独自のものを構築できますパッケージとプラグインの開発ページ。

Flutter のプラグイン アーキテクチャは、一言で言えば、 Android: メッセージを送信し、受信者に結果を処理させて出力させます。 あなたに戻って。この場合、受信側はネイティブ側で実行されるコードです。 Android または iOS で。

Flutter アプリケーションで NDK を使用するにはどうすればよいですか?

現在の Android アプリケーションで NDK を使用していて、Flutter が必要な場合 アプリケーションがネイティブ ライブラリを利用できるようにするには、 カスタムプラグインを構築します。

カスタム プラグインはまず Android アプリと通信し、そこでnativeJNI を介して機能します。返答の準備ができたら、 Flutter にメッセージを送り返し、結果をレンダリングします。

Flutter から直接ネイティブ コードを呼び出すことは現在サポートされていません。

テーマ

アプリのテーマを設定するにはどうすればよいですか?

Flutter には、すぐに使えるマテリアルの美しい実装が付属しています。 デザイン: 多くのスタイルやテーマのニーズに対応します。 通常はそうします。 XML でテーマを宣言して割り当てる Android とは異なります。 AndroidManifest.xml を使用してアプリケーションに、Flutter でテーマを宣言します 最上位のウィジェット内。

アプリでマテリアル コンポーネントを最大限に活用するには、トップを宣言できます。 レベルウィジェットMaterialAppアプリケーションへのエントリ ポイントとして。マテリアルアプリ 一般的に使用されるいくつかのウィジェットをラップする便利なウィジェットです。 マテリアル デザインを実装するアプリケーションに必要です。 これは、マテリアル固有の機能を追加することにより、WidgetsApp に基づいて構築されます。

を使用することもできますWidgetsAppアプリのウィジェットとして使用すると、次のような機能が提供されます。 同じ機能ですが、それほど豊富ではありませんMaterialApp

子コンポーネントの色とスタイルをカスタマイズするには、ThemeDataに反対するMaterialAppウィジェット。たとえば、以下のコードでは、 プライマリ スウォッチは青に設定され、テキスト選択の色は赤に設定されます。

import 'package:flutter/material.dart';

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        textSelectionTheme:
            const TextSelectionThemeData(selectionColor: Colors.red),
      ),
      home: const SampleAppPage(),
    );
  }
}

データベースとローカルストレージ

共有設定にアクセスするにはどうすればよいですか?

Android では、次を使用してキーと値のペアの小さなコレクションを保存できます。 SharedPreferences API。

Flutter では、次のコマンドを使用してこの機能にアクセスします。Shared_Preferences プラグイン。 このプラグインは両方の機能をラップします。 共有設定と NSUserDefaults (iOS に相当)。

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

import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            onPressed: _incrementCounter,
            child: Text('Increment Counter'),
          ),
        ),
      ),
    ),
  );
}

Future<void> _incrementCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  int counter = (prefs.getInt('counter') ?? 0) + 1;
  await prefs.setInt('counter', counter);
}

Flutter で SQLite にアクセスするにはどうすればよいですか?

Android では、SQLite を使用してクエリ可能な構造化データを保存します。 SQLを使用して。

Flutter では、次のコマンドを使用してこの機能にアクセスします。SQフライトプラグイン。

デバッグ

Flutter でアプリをデバッグするにはどのようなツールを使用できますか?

使用開発ツールFlutter または Dart アプリをデバッグするためのスイート。

DevTools には、プロファイリング、ヒープの検査、 ウィジェット ツリーの検査、診断のログ記録、デバッグ、 実行されたコード行の監視、メモリ リークとメモリのデバッグ 断片化。詳細については、「開発ツールドキュメンテーション。

通知

プッシュ通知を設定するにはどうすればよいですか?

Android では、Firebase Cloud Messaging を使用してプッシュを設定します アプリの通知。

Flutter では、次のコマンドを使用してこの機能にアクセスします。Firebaseメッセージングプラグイン。 Firebase Cloud Messaging API の使用方法の詳細については、 を参照してくださいfirebase_messagingプラグインのドキュメント。