このドキュメントは、既存のRN知識を適用してFlutterを使用してモバイルアプリを構築しようとしているReact Native(RN)開発者を対象としています。 RNフレームワークの基本を理解している場合は、このドキュメントをFlutter開発の学習を開始する方法として使用できます。
このドキュメントは、ジャンプしてニーズに最も関連する質問を見つけることにより、クックブックとして使用できます。
Contents
Introduction to Dart for JavaScript Developers
JavaScript開発者向けのDartの紹介
React Nativeと同様に、Flutterはリアクティブスタイルのビューを使用します。 ただし、RNはネイティブウィジェットに変換されますが、Flutterはネイティブコードまでコンパイルされます。 Flutterは画面上の各ピクセルを制御し、JavaScriptブリッジの必要性によって引き起こされるパフォーマンスの問題を回避します。
Dartは習得が容易な言語であり、次の機能を提供します。
- Web、サーバー、およびモバイルアプリを構築するためのオープンソースのスケーラブルなプログラミング言語を提供します。
- ネイティブにAOTコンパイルされたCスタイルの構文を使用するオブジェクト指向の単一継承言語を提供します。
- オプションでJavaScriptにトランスコンパイルします。
- インターフェイスと抽象クラスをサポートします。
JavaScriptとDartの違いのいくつかの例を以下に説明します。
Entry point
JavaScriptには、事前定義されたエントリ関数はありません。エントリポイントを定義します。
// JavaScript
function startHere() {
// Can be used as entry point
}
Dartでは、すべてのアプリに、アプリへのエントリポイントとして機能するトップレベルのmain()関数が必要です。
// Dart
main() {
}
DartPadでお試しください。
Printing to the console
Dartでコンソールに表示するには、print()を使用します。
// JavaScript
console.log('Hello world!');
// Dart
print('Hello world!');
DartPadでお試しください。
Variables
Dartは型セーフです。静的型チェックと実行時チェックの組み合わせを使用して、変数の値が常に変数の静的型と一致するようにします。
型は必須ですが、Dartが型推論を実行するため、一部の型注釈はオプションです。
Creating and assigning variables
JavaScriptでは、変数に型付けすることはできません。
Dartでは、変数を明示的に型指定するか、型システムが適切な型を自動的に推測する必要があります。
// JavaScript
var name = 'JavaScript';
// Dart
String name = 'dart'; // String型と明示的に示す。
var otherName = 'Dart'; // String型と推論される。
// Dartでは両方可能です。
DartPadでお試しください。
さらに詳しい情報は、Dart’s Type Systemをご覧ください。
Default value
JavaScriptでは、初期化されていない変数はundefinedです。
Dartでは、初期化されていない変数の初期値はnullです。
数値はDartのオブジェクトであるため、数値型の初期化されていない変数でも値はnullになります。
// JavaScript
var name; // == undefined
// Dart
var name; // == null
int x; // == null
DartPadでお試しください。
さらに詳しい情報は、variablesをご覧ください。
Checking for null or zero
JavaScriptでは、1またはnull以外のオブジェクトの値はtrueとして扱われます。
// JavaScript
var myNull = null;
if (!myNull) {
console.log('null is treated as false');
}
var zero = 0;
if (!zero) {
console.log('0 is treated as false');
}
Dartでは、ブール値trueのみがtrueとして扱われます。
// Dart
var myNull = null;
if (myNull == null) {
print('use "== null" to check null');
}
var zero = 0;
if (zero == 0) {
print('use "== 0" to check zero');
}
DartPadでお試しください。
Functions
Dart関数とJavaScript関数は一般的に似ています。 主な違いは宣言です。
// JavaScript
function fn() {
return true;
}
// Dart
fn() {
return true;
}
// can also be written as
bool fn() {
return true;
}
DartPadでお試しください。
さらに詳しい情報は、functionsをご覧ください。
Asynchronous programming
Futures
JavaScriptと同様に、Dartはシングルスレッド実行をサポートしています。 JavaScriptでは、Promiseオブジェクトは、非同期操作の最終的な完了(または失敗)とその結果の値を表します。
DartはFutureオブジェクトを使用してこれを処理します。
// JavaScript
class Example {
_getIPAddress() {
const url = 'https://httpbin.org/ip';
return fetch(url)
.then(response => response.json())
.then(responseJson => {
const ip = responseJson.origin;
return ip;
});
}
}
function main() {
const example = new Example();
example
._getIPAddress()
.then(ip => console.log(ip))
.catch(error => console.error(error));
}
main();
// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Example {
Future<String> _getIPAddress() {
final url = 'https://httpbin.org/ip';
return http.get(url).then((response) {
String ip = jsonDecode(response.body)['origin'];
return ip;
});
}
}
main() {
final example = new Example();
example
._getIPAddress()
.then((ip) => print(ip))
.catchError((error) => print(error));
}
詳細については、Futureオブジェクトに関するドキュメントを参照してください。
async
and await
async関数の宣言は、非同期関数を定義します。
JavaScriptでは、async関数はPromiseを返します。 await演算子は、Promiseを待機するために使用されます。
// JavaScript
class Example {
async function _getIPAddress() {
const url = 'https://httpbin.org/ip';
const response = await fetch(url);
const json = await response.json();
const data = json.origin;
return data;
}
}
async function main() {
const example = new Example();
try {
const ip = await example._getIPAddress();
console.log(ip);
} catch (error) {
console.error(error);
}
}
main();
Dartでは、非同期関数はFutureを返し、関数の本体は後で実行されるようにスケジュールされています。
await演算子は、Futureを待機するために使用されます。
// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Example {
Future<String> _getIPAddress() async {
final url = 'https://httpbin.org/ip';
final response = await http.get(url);
String ip = jsonDecode(response.body)['origin'];
return ip;
}
}
main() async {
final example = new Example();
try {
final ip = await example._getIPAddress();
print(ip);
} catch (error) {
print(error);
}
}
詳細については、async and awaitに関するドキュメントを参照してください。
The basics
How do I create a Flutter app?
React Nativeを使用してアプリを作成するには、コマンドラインからcreate-react-native-appを実行します。
$ create-react-native-app <projectname>
Flutterでアプリを作成するには、次のいずれかを実行します。
- FlutterおよびDartプラグインがインストールされたIDEを使用します。
- コマンドラインからfluttercreateコマンドを使用します。 FlutterSDKがPATHに含まれていることを確認してください。
$ flutter create <projectname>
詳細については、「Getting started」を参照してください。このガイドでは、ボタンクリックカウンターアプリの作成について説明しています。 Flutterプロジェクトを作成すると、AndroidデバイスとiOSデバイスの両方でサンプルアプリを実行するために必要なすべてのファイルがビルドされます。
How do I run my app?
React Nativeでは、プロジェクトディレクトリからnpm runまたはyarn runを実行します。
Flutterアプリはいくつかの方法で実行できます。
- FlutterおよびDartプラグインを備えたIDEで「実行」オプションを使用します。
- プロジェクトのルートディレクトリからflutter runを実行します。
アプリは、接続されたデバイス、iOSシミュレーター、またはAndroidエミュレーターで実行されます。
詳細については、Flutter入門ドキュメントを参照してください。
How do I import widgets?
React Nativeでは、必要な各コンポーネントをインポートする必要があります。
//React Native
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
Flutterで、マテリアルデザインライブラリのウィジェットを使用するには、material.dartパッケージをインポートします。 iOSスタイルのウィジェットを使用するには、Cupertinoライブラリをインポートします。 より基本的なウィジェットセットを使用するには、ウィジェットライブラリをインポートします。 または、独自のウィジェットライブラリを作成してインポートすることもできます。
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/my_widgets.dart';
どのウィジェットパッケージをインポートしても、Dartはアプリで使用されているウィジェットのみを取り込みます。
詳細については、Flutterウィジェットカタログを参照してください。
What is the equivalent of the React Native “Hello world!” app in Flutter?
FlutterでReact Nativeの「Helloworld!」に相当するものは何ですか。
React Nativeでは、HelloWorldAppクラスはReact.Componentを拡張し、ビューコンポーネントを返すことでrenderメソッドを実装します。
// React Native
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Hello world!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
}
});
Flutterでは、同じ「Helloworld!」を作成できます。 コアウィジェットライブラリのCenterウィジェットとTextウィジェットを使用するアプリ。 Centerウィジェットはウィジェットツリーのルートになり、Textウィジェットという1つの子があります。
// Flutter
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
次の画像は、基本的なFlutter「Helloworld!」のAndroid示しています。
最も基本的なFlutterアプリを見てきたので、次のセクションでは、Flutterの豊富なウィジェットライブラリを利用して、最新の洗練されたアプリを作成する方法を示します。
How do I use widgets and nest them to form a widget tree?
ウィジェットを使用してネストし、ウィジェットツリーを形成するにはどうすればよいですか?
Flutterでは、ほとんどすべてがウィジェットです。
ウィジェットは、アプリのユーザーインターフェースの基本的な構成要素です。 ウィジェットは、ウィジェットツリーと呼ばれる階層に構成されます。 各ウィジェットは親ウィジェット内にネストし、その親からプロパティを継承します。
アプリケーションオブジェクト自体もウィジェットです。 個別の「アプリケーション」オブジェクトはありません。 代わりに、ルートウィジェットがこの役割を果たします。
ウィジェットは以下を定義できます。
- ボタンやメニューなどの構造要素
- フォントや配色などのスタイル要素
- パディングや配置などのレイアウトの側面
次の例は、「Helloworld!」を示しています。 マテリアルライブラリのウィジェットを使用するアプリです。
この例では、ウィジェットツリーはMaterialAppルートウィジェット内にネストされています。
// Flutter
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello world'),
),
),
);
}
}
次の画像は「Helloworld!」を示しています。 マテリアルデザインウィジェットから構築されています。 基本的な「Helloworld!」アプリよりも多くの機能を自由に利用できます。
アプリを作成するときは、StatelessWidgetまたはStatefulWidgetの2種類のウィジェットを使用します。 StatelessWidgetは、まさにその名の通り、状態のないウィジェットです。 StatelessWidgetは一度作成され、外観が変わることはありません。
StatefulWidgetは、受信したデータまたはユーザー入力に基づいて状態を動的に変更します。
ステートレスウィジェットとステートフルウィジェットの重要な違いは、StatefulWidgetsには、状態データを格納し、ツリーの再構築全体に引き継ぐStateオブジェクトがあるため、状態が失われないことです。
シンプルなアプリや基本的なアプリでは、ウィジェットをネストするのは簡単ですが、コードベースが大きくなり、アプリが複雑になると、深くネストされたウィジェットを、ウィジェットまたは小さいクラスを返す関数に分割する必要があります。 個別の関数とウィジェットを作成すると、アプリ内でコンポーネントを再利用できます。
How do I create reusable components?
再利用可能なコンポーネントを作成するにはどうすればよいですか?
React Nativeでは、クラスを定義して再利用可能なコンポーネントを作成し、propsメソッドを使用して、選択した要素のプロパティと値を設定または返します。 以下の例では、CustomCardクラスが定義され、親クラス内で使用されています。
// React Native
class CustomCard extends React.Component {
render() {
return (
<View>
<Text> Card {this.props.index} </Text>
<Button
title="Press"
onPress={() => this.props.onPress(this.props.index)}
/>
</View>
);
}
}
// Usage
<CustomCard onPress={this.onPress} index={item.key} />
Flutterで、クラスを定義してカスタムウィジェットを作成し、ウィジェットを再利用します。 次の例のbuild関数に示すように、再利用可能なウィジェットを返す関数を定義して呼び出すこともできます。
// Flutter
class CustomCard extends StatelessWidget {
//名前付きパラメータ↓
CustomCard({@required this.index, @required
this.onPress});
final index;
final Function onPress;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
TextButton(
child: const Text('Press'),
onPressed: this.onPress,
),
],
)
);
}
}
...
// Usage
CustomCard(
index: index,
onPress: () {
print('Card $index');
},
)
...
↑の例では、CustomCardクラスのコンストラクターは、Dartの中括弧構文{}を使用して、名前付きオプションパラメーターを示しています。
これらのフィールドを必須にするには、コンストラクターから中括弧を削除するか、コンストラクターに@requiredを追加します。
次のスクリーンショットは、再利用可能なCustomCardクラスの例を示しています。
参考
https://flutter.dev/docs/get-started/flutter-for/react-native-devs