Flutterは、柔軟で表現力のあるクロスプラットフォームモバイルアプリケーションを作成するためのGoogleのUIフレームワークです。これは、モバイルアプリケーション開発のための最も急速に成長しているフレームワークの1つです。一方、動物相は、ネイティブGraphQLをサポートするトランザクションの開発者に優しいサーバーレスデータベースです。フラッターファウナは天国によって作られた完璧なマッチです。レコードタイムで機能が豊富なフルスタックアプリケーションを構築およびリリースする場合は、FlutterとFaunaが適切なツールです。この記事では、動物相とGraphQLバックエンドを使用して、最初のフラッターアプリケーションの構築について説明します。
この記事の完全なコードはGithubで見つけることができます。
この記事を読んだ後、あなたは次の方法を知っている必要があります
Fauna vs. AWS Amplify vs. Firebase :Faunaはどのような問題を解決しますか?他のサーバーレスソリューションとどう違うのですか?動物相に精通しておらず、ファウナと他のソリューションとの比較についてもっと知りたい場合は、この記事を読むことをお勧めします。
ユーザーがお気に入りの映画やテレビシリーズのキャラクターを追加、削除、更新できるシンプルなモバイルアプリを構築します。
fauna.comにアクセスして、新しいアカウントを作成します。ログインした後、新しいデータベースを作成できるはずです。
データベースに名前を付けます。私は私のflutter_demoという名前を付けました。次に、地域グループを選択できます。このデモでは、クラシックを選択します。 Faunaは、グローバルに分散されたサーバーレスデータベースです。これは、どこからでも低遅延の読み取りおよび書き込みアクセスをサポートする唯一のデータベースです。それはCDN(コンテンツ配信ネットワーク)と考えてください。しかし、それはあなたのデータベース用です。地域グループの詳細については、このガイドをフォローしてください。
データベースが作成されたら、[セキュリティ]タブに移動します。 [新しいキー]ボタンをクリックして、データベースの新しいキーを作成します。 GraphQL操作に必要なため、このキーを適切に保持してください。
データベースの管理者キーを作成します。管理者の役割を備えたキーは、データベースアクセスプロバイダー、亜データベース、ドキュメント、関数、インデックス、キー、トークン、ユーザー定義の役割など、関連するデータベースを管理するために使用されます。 Faunaのさまざまなセキュリティキーの詳細と、以下のリンクでアクセスロールをご覧ください。
ユーザーがお気に入りのテレビキャラクターを追加、更新、削除できるシンプルなアプリを構築します。
次のコマンドを実行して、新しいフラッタープロジェクトを作成しましょう。
<code>flutter create my_app</code>
プロジェクトディレクトリでは、graphql/schema.graphqlという名前の新しいファイルを作成します。
スキーマファイルでは、コレクションの構造を定義します。動物相のコレクションは、SQLの表に似ています。今は1セットしか必要ありません。私たちはそれをキャラクターと名付けます。
<code>### schema.graphql type Character { name: String! description: String! picture: String } type Query { listAllCharacters: [Character] }</code>
上記のように、複数のプロパティ(つまり、名前、説明、画像など)を持つ文字と呼ばれるタイプを定義します。プロパティは、SQLデータベースの列またはNOSQLデータベースのキー価値ペアとして扱うことができます。また、クエリを定義します。このクエリはロールリストを返します。
それでは、ファウナダッシュボードに戻りましょう。 [GraphQL]をクリックし、[インポートモード]をクリックして、モードを動物相にアップロードします。
インポートが完了した後、FaunaがGraphQLクエリと突然変異を生成することがわかります。
GraphQLの自動生成が好きではありませんか?あなたのビジネスロジックをよりよく制御したいですか?この場合、FAUNAでは、カスタムGraphQLパーサーを定義できます。詳細については、このリンクをクリックしてください。
pubspec.yamlファイルを開き、必要な依存関係を追加しましょう。
<code>... dependencies: graphql_flutter: ^4.0.0-beta hive: ^1.3.0 flutter: sdk: flutter ...</code>
ここに2つの依存関係を追加しました。 GraphQL_FlutterはFlutterのGraphQLクライアントライブラリです。 GraphQLクライアントのすべての最新機能を、使いやすいパッケージに統合します。また、Hiveパッケージを依存関係として追加しました。 Hiveは、ローカルストレージのために純粋なDARTで記述された軽量のキー価値データベースです。 Hiveを使用して、GraphQLクエリをキャッシュします。
次に、新しいファイルlib/client_provider.dartを作成します。ファウナ構成を含むプロバイダークラスをそのファイルに作成します。
FaunaのGraphQL APIに接続するには、最初にGraphQlClientを作成する必要があります。 GraphQlClientには、キャッシュとリンクを初期化する必要があります。以下のコードを見てみましょう。
<code>// lib/client_provider.dart import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:flutter/material.dart'; ValueNotifier<graphqlclient> clientFor({ @required String uri, String subscriptionUri, }) { final HttpLink httpLink = HttpLink( uri, ); final AuthLink authLink = AuthLink( getToken: () async => 'Bearer fnAEPAjy8QACRJssawcwuywad2DbB6ssrsgZ2-2', ); Link link = authLink.concat(httpLink); return ValueNotifier<graphqlclient> ( GraphQLClient( cache: GraphQLCache(store: HiveStore()), link: link, ), ); }</graphqlclient></graphqlclient></code>
上記のコードでは、graphqlclientをラップするvalueNotifierを作成します。 13〜15行目(強調表示)でAuthLinkを構成したことに注意してください。 14行目で、トークンの一部として動物相の管理者キーを追加しました。ここでは、管理キーをハードコードしました。ただし、生産アプリケーションでは、動物相のセキュリティキーをハードコードしないようにする必要があります。
Flutterアプリケーションにキーを保存する方法はいくつかあります。参照については、このブログを確認してください。
アプリケーション内の任意のウィジェットからクエリと突然変異を呼び出すことができます。これを行うには、GraphQLProviderウィジェットを使用してウィジェットをラップする必要があります。
<code>// lib/client_provider.dart .... /// 使用`graphql_flutter`客户端包装根应用程序。 /// 我们使用缓存进行所有状态管理。 class ClientProvider extends StatelessWidget { ClientProvider({ @required this.child, @required String uri, }) : client = clientFor( uri: uri, ); final Widget child; final ValueNotifier<graphqlclient> client; @override Widget build(BuildContext context) { return GraphQLProvider( client: client, child: child, ); } }</graphqlclient></code>
次に、main.dartファイルに移動し、メインウィジェットをClientProviderウィジェットでラップします。以下のコードを見てみましょう。
<code>// lib/main.dart ... void main() async { await initHiveForFlutter(); runApp(MyApp()); } final graphqlEndpoint = 'https://graphql.fauna.com/graphql'; class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ClientProvider( uri: graphqlEndpoint, child: MaterialApp( title: 'My Character App', debugShowCheckedModeBanner: false, initialRoute: '/', routes: { '/': (_) => AllCharacters(), '/new': (_) => NewCharacter(), } ), ); } }</code>
この時点で、すべてのダウンストリームウィジェットはクエリと変異関数を実行でき、GraphQL APIと対話できます。
デモアプリケーションは、シンプルで理解しやすいものでなければなりません。先に進み、すべての役割のリストを表示するシンプルなリストウィジェットを作成しましょう。新しいファイルlib/screens/character-list.dartを作成しましょう。このファイルでは、AllCharactersという新しいウィジェットを書き込みます。
<code>// lib/screens/character-list.dart.dart class AllCharacters extends StatelessWidget { const AllCharacters({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: [ SliverAppBar( pinned: true, snap: false, floating: true, expandedHeight: 160.0, title: Text( 'Characters', style: TextStyle( fontWeight: FontWeight.w400, fontSize: 36, ), ), actions:<widget> [ IconButton( padding: EdgeInsets.all(5), icon: const Icon(Icons.add_circle), tooltip: 'Add new entry', onPressed: () { Navigator.pushNamed(context, '/new'); }, ), ], ), SliverList( delegate: SliverChildListDelegate([ Column( children: [ for (var i = 0; i _CharacterTileeState(); } class _CharacterTileState extends State<charactertile> { @override Widget build(BuildContext context) { return Container( child: Text("Character Tile"), ); } }</charactertile></widget></code>
上記のコードに示されているように、[37行目]は、リストにいくつかの偽のデータを埋めるループ用です。最後に、FaunaバックエンドでGraphQLクエリを実行し、データベースからすべての役割を取得します。これを行う前に、アプリケーションを実行してみましょう。次のコマンドを使用してアプリケーションを実行できます
<code>flutter run</code>
この時点で、次の画面を確認できるはずです。
これで、GraphQLクエリに接続し続けることができる基本的なウィジェットがいくつかあります。ハードコーディングされた文字列の代わりに、データベースからすべての役割を取得し、AllCharactersウィジェットで表示したいと考えています。
動物相のGraphQL遊び場に戻りましょう。次のクエリを実行して、すべての役割をリストできることに注意してください。
<code>query ListAllCharacters { listAllCharacters(_size: 100) { data { _id name description picture } after } }</code>
ウィジェットからこのクエリを実行するには、いくつかの変更を加える必要があります。
<code>import 'package:flutter/material.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:todo_app/screens/Character-tile.dart'; String readCharacters = ";";"; query ListAllCharacters { listAllCharacters(_size: 100) { data { _id name description picture } after } } ";";";; class AllCharacters extends StatelessWidget { const AllCharacters({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: [ SliverAppBar( pinned: true, snap: false, floating: true, expandedHeight: 160.0, title: Text( 'Characters', style: TextStyle( fontWeight: FontWeight.w400, fontSize: 36, ), ), actions:<widget> [ IconButton( padding: EdgeInsets.all(5), icon: const Icon(Icons.add_circle), tooltip: 'Add new entry', onPressed: () { Navigator.pushNamed(context, '/new'); }, ), ], ), SliverList( delegate: SliverChildListDelegate([ Query(options: QueryOptions( document: gql(readCharacters), // 我们要执行的graphql查询pollInterval: Duration(seconds: 120), // 重新获取间隔), builder: (QueryResult result, { VoidCallback refetch, FetchMore fetchMore }) { if (result.isLoading) { return Text('Loading'); } return Column( children: [ for (var item in result.data\['listAllCharacters'\]['data']) CharacterTile(Character: item, refetch: refetch), ], ); }) ]) ) ], ), ); } }</widget></code>
最初に、データベースからすべての役割を取得するためにクエリ文字列を定義します[行5から17]。 Flutter_graphqlのクエリウィジェットを使用して、リストウィジェットをラップします。
Flutter_graphqlライブラリの公式ドキュメントをお気軽にご覧ください。
クエリオプションパラメーターでは、GraphQLクエリ文字列自体を提供します。 Pollinterintervalパラメーターのフローティングポイント番号を渡すことができます。世論調査の間隔は、バックエンドからデータを返したい時間を定義します。ウィジェットには標準のビルダー機能もあります。ビルダー関数を使用して、クエリの結果を渡し、コールバック関数を再ゲットし、ウィジェットツリーにより多くのコールバック関数を取得できます。
次に、キャラクターのウィジェットを更新して、画面に文字データを表示します。
<code>// lib/screens/character-tile.dart ... class CharacterTile extends StatelessWidget { final Character; final VoidCallback refetch; final VoidCallback updateParent; const CharacterTile({ Key key, @required this.Character, @required this.refetch, this.updateParent, }) : super(key: key); @override Widget build(BuildContext context) { return InkWell( onTap: () { }, child: Padding( padding: const EdgeInsets.all(10), child: Row( children: [ Container( height: 90, width: 90, decoration: BoxDecoration( color: Colors.amber, borderRadius: BorderRadius.circular(15), image: DecorationImage( fit: BoxFit.cover, image: NetworkImage(Character['picture']) ) ), ), SizedBox(width: 10), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( Character['name'], style: TextStyle( color: Colors.black87, fontWeight: FontWeight.bold, ), ), SizedBox(height: 5), Text( Character['description'], style: TextStyle( color: Colors.black87, ), maxLines: 2, ), ], ) ) ], ), ), ); } }</code>
次の突然変異を実行することにより、データベースに新しい役割を追加できます。
<code>mutation CreateNewCharacter($data: CharacterInput!) { createCharacter(data: $data) { _id name description picture } }</code>
ウィジェットからこの突然変異を実行するには、Flutter_graphqlライブラリのMutation Widgetを使用する必要があります。ユーザーが対話してデータを入力できるように、シンプルなフォームを備えた新しいウィジェットを作成しましょう。フォームを送信した後、CreateCharacter変異が呼び出されます。
<code>// lib/screens/new.dart ... String addCharacter = ";";"; mutation CreateNewCharacter(\$data: CharacterInput!) { createCharacter(data: \$data) { _id name description picture } } ";";";; class NewCharacter extends StatelessWidget { const NewCharacter({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Add New Character'), ), body: AddCharacterForm() ); } } class AddCharacterForm extends StatefulWidget { AddCharacterForm({Key key}) : super(key: key); @override _AddCharacterFormState createState() => _AddCharacterFormState(); } class _AddCharacterFormState extends State<addcharacterform> { String name; String description; String imgUrl; @override Widget build(BuildContext context) { return Form( child: Padding( padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( decoration: const InputDecoration( icon: Icon(Icons.person), labelText: 'Name *', ), onChanged: (text) { name = text; }, ), TextField( decoration: const InputDecoration( icon: Icon(Icons.post_add), labelText: 'Description', ), minLines: 4, maxLines: 4, onChanged: (text) { description = text; }, ), TextField( decoration: const InputDecoration( icon: Icon(Icons.image), labelText: 'Image Url', ), onChanged: (text) { imgUrl = text; }, ), SizedBox(height: 20), Mutation( options: MutationOptions( document: gql(addCharacter), onCompleted: (dynamic resultData) { print(resultData); name = ''; description = ''; imgUrl = ''; Navigator.of(context).push( MaterialPageRoute(builder: (context) => AllCharacters()) ); }, ), builder: ( RunMutation runMutation, QueryResult result, ) { return Center( child: ElevatedButton( child: const Text('Submit'), onPressed: () { runMutation({ 'data': { ";picture";: imgUrl, ";name";: name, ";description";: description, } }); }, ), ); } ) ], ), ), ); } }</addcharacterform></code>
上記のコードからわかるように、Mutationウィジェットはクエリウィジェットと非常に同様に機能します。さらに、Mutationウィジェットは、oncomplete機能を提供します。この関数は、変異が完了した後、データベースの更新結果を返します。
DeleteCharacter変異を実行することにより、データベースから役割を削除できます。このミュータントをキャラクターに追加し、ボタンが押されたときにトリガーできます。
<code>// lib/screens/character-tile.dart ... String deleteCharacter = ";";"; mutation DeleteCharacter(\$id: ID!) { deleteCharacter(id: \$id) { _id name } } ";";";; class CharacterTile extends StatelessWidget { final Character; final VoidCallback refetch; final VoidCallback updateParent; const CharacterTile({ Key key, @required this.Character, @required this.refetch, this.updateParent, }) : super(key: key); @override Widget build(BuildContext context) { return InkWell( onTap: () { showModalBottomSheet( context: context, builder: (BuildContext context) { print(Character['picture']); return Mutation( options: MutationOptions( document: gql(deleteCharacter), onCompleted: (dynamic resultData) { print(resultData); this.refetch(); }, ), builder: ( RunMutation runMutation, QueryResult result, ) { return Container( height: 400, padding: EdgeInsets.all(30), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children:<widget> [ Text(Character['description']), ElevatedButton( child: Text('Delete Character'), onPressed: () { runMutation({ 'id': Character['_id'], }); Navigator.pop(context); }, ), ], ), ), ); } ); } ); }, child: Padding( padding: const EdgeInsets.all(10), child: Row( children: [ Container( height: 90, width: 90, decoration: BoxDecoration( color: Colors.amber, borderRadius: BorderRadius.circular(15), image: DecorationImage( fit: BoxFit.cover, image: NetworkImage(Character['picture']) ) ), ), SizedBox(width: 10), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( Character['name'], style: TextStyle( color: Colors.black87, fontWeight: FontWeight.bold, ), ), SizedBox(height: 5), Text( Character['description'], style: TextStyle( color: Colors.black87, ), maxLines: 2, ), ], ) ) ], ), ), ); } }</widget></code>
データの編集は、追加と削除と同じです。 GraphQL APIの別の突然変異です。新しいロールフォームウィジェットと同様の編集ロールフォームウィジェットを作成できます。唯一の違いは、フォームの編集がUpdateCharacter変異を実行することです。編集用に、新しいウィジェットLIB/SCREENS/EDIT.DARTを作成しました。これがこのウィジェットのコードです。
<code>// lib/screens/edit.dart String editCharacter = """ mutation EditCharacter(\$name: String!, \$id: ID!, \$description: String!, \$picture: String!) { updateCharacter(data: { name: \$name description: \$description picture: \$picture }, id: \$id) { _id name description picture } } """; class EditCharacter extends StatelessWidget { final Character; const EditCharacter({Key key, this.Character}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Edit Character'), ), body: EditFormBody(Character: this.Character), ); } } class EditFormBody extends StatefulWidget { final Character; EditFormBody({Key key, this.Character}) : super(key: key); @override _EditFormBodyState createState() => _EditFormBodyState(); } class _EditFormBodyState extends State<editformbody> { String name; String description; String picture; @override Widget build(BuildContext context) { return Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( initialValue: widget.Character['name'], decoration: const InputDecoration( icon: Icon(Icons.person), labelText: 'Name *', ), onChanged: (text) { name = text; } ), TextFormField( initialValue: widget.Character['description'], decoration: const InputDecoration( icon: Icon(Icons.person), labelText: 'Description', ), minLines: 4, maxLines: 4, onChanged: (text) { description = text; } ), TextFormField( initialValue: widget.Character['picture'], decoration: const InputDecoration( icon: Icon(Icons.image), labelText: 'Image Url', ), onChanged: (text) { picture = text; }, ), SizedBox(height: 20), Mutation( options: MutationOptions( document: gql(editCharacter), onCompleted: (dynamic resultData) { print(resultData); Navigator.of(context).push( MaterialPageRoute(builder: (context) => AllCharacters()) ); }, ), builder: ( RunMutation runMutation, QueryResult result, ) { print(result); return Center( child: ElevatedButton( child: const Text('Submit'), onPressed: () { runMutation({ 'id': widget.Character['_id'], 'name': name != null ? name : widget.Character['name'], 'description': description != null ? description : widget.Character['description'], 'picture': picture != null ? picture : widget.Character['picture'], }); }, ), ); } ), ] ) ), ); } }</editformbody></code>
この記事の完全なコードを次のように表示できます。
動物相やフラッターについて質問がありますか? Twitter @Haqueshadidで私に連絡できます
github ###次のステップ
この記事の主な目的は、FlutterとFaunaを始めることです。ここでのみ表面に触れます。 Fauna Ecosystemは、モバイルアプリケーションにサービスとして完全な自動スケーリング、開発者に優しいバックエンドを提供します。あなたの目標が、記録的な時期に生産に使用できるクロスプラットフォームモバイルアプリをリリースすることである場合は、 FaunaとFlutterを試してください。
Faunaの公式ドキュメントWebサイトをチェックすることを強くお勧めします。 DART/FlutterのGraphQLクライアントについて詳しく知りたい場合は、GraphQL_Flutterの公式GitHubリポジトリをご覧ください。
私はあなたに幸せなプログラミングを願っています、次回もまた会いましょう。
以上がFlutter、Fauna、およびGraphQLを使用してフルスタックモバイルアプリケーションを構築する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。