Flutter ist das UI-Framework von Google zum Erstellen flexibler, ausdrucksstarker plattformübergreifender mobiler Anwendungen. Es ist eines der am schnellsten wachsenden Rahmen für die Entwicklung der mobilen Anwendungen. Fauna hingegen ist eine transaktionale, entwicklerfreundliche serverlose Datenbank, die native GraphQL unterstützt. Flutter Fauna ist ein perfektes Spiel vom Himmel. Wenn Sie Funktionen in der Aufzeichnungszeit erstellen und veröffentlicht möchten, sind Flutter und Fauna die richtigen Werkzeuge. In diesem Artikel führen wir Sie durch den Bau Ihrer ersten Flutter -Anwendung mit Fauna und GraphQL -Backends.
Sie finden den vollständigen Code für diesen Artikel auf GitHub.
Nach dem Lesen dieses Artikels sollten Sie wissen, wie Sie:
Fauna vs. AWS Amplify vs. Firebase : Welche Probleme lösen Fauna? Wie unterscheidet es sich von anderen serverlosen Lösungen? Wenn Sie mit Fauna nicht vertraut sind und mehr über Faunas Vergleich mit anderen Lösungen erfahren möchten, schlage ich vor, dass Sie diesen Artikel lesen.
Wir werden eine einfache mobile App erstellen, mit der Benutzer ihre bevorzugten Film- und TV -Serienfiguren hinzufügen, löschen und aktualisieren können.
Gehen Sie zu Fauna.com und erstellen Sie ein neues Konto. Nach dem Anmelden sollten Sie in der Lage sein, eine neue Datenbank zu erstellen.
Nennen Sie Ihre Datenbank. Ich habe meinen Flutter_Demo genannt. Als nächstes können wir eine Regionsgruppe auswählen. Für diese Demo werden wir Classic auswählen. Fauna ist eine global verteilte serverlose Datenbank. Es ist die einzige Datenbank, die von überall mit dem Lese- und Schreibzugriff mit geringer Latenz unterstützt wird. Stellen Sie sich es als CDN (Content Distribution Network) vor, aber es ist für Ihre Datenbank. Um mehr über die Region -Gruppe zu erfahren, folgen Sie diesem Leitfaden.
Sobald die Datenbank erstellt wurde, wechseln Sie zur Registerkarte Sicherheit. Klicken Sie auf die Schaltfläche Neue Taste und erstellen Sie einen neuen Schlüssel für Ihre Datenbank. Bitte behalten Sie diesen Schlüssel ordnungsgemäß, da wir ihn für GraphQL -Operationen benötigen.
Wir erstellen einen Administratorschlüssel für unsere Datenbank. Ein Schlüssel mit einer Administratorrolle wird verwendet, um die zugehörigen Datenbanken zu verwalten, einschließlich Datenbankzugriffsanbietern, Subdatabasen, Dokumenten, Funktionen, Indizes, Tasten, Token und benutzerdefinierten Rollen. Weitere Informationen zu den verschiedenen Sicherheitsschlüssel von Fauna und Zugriffsrollen finden Sie in den folgenden Links.
Wir erstellen eine einfache App, mit der Benutzer ihre bevorzugten Fernsehfiguren hinzufügen, aktualisieren und löschen können.
Erstellen wir ein neues Flutter -Projekt, indem wir den folgenden Befehl ausführen.
<code>flutter create my_app</code>
Im Projektverzeichnis erstellen wir eine neue Datei namens GraphQL/Schema.Graphql.
In der Schema -Datei definieren wir die Struktur der Sammlung. Sammlungen in Fauna ähneln den Tabellen in SQL. Wir brauchen jetzt nur einen Satz. Wir nennen es Charakter.
<code>### schema.graphql type Character { name: String! description: String! picture: String } type Query { listAllCharacters: [Character] }</code>
Wie oben gezeigt, definieren wir einen Typ, der als Zeichen bezeichnet wird und mehrere Eigenschaften aufweist (d. H. Name, Beschreibung, Bild usw.). Eine Eigenschaft kann als Spalte einer SQL-Datenbank oder als Schlüsselwertpaar einer NoSQL-Datenbank behandelt werden. Wir definieren auch eine Frage. Diese Abfrage gibt die Rollenliste zurück.
Kehren wir jetzt zurück zum Fauna Dashboard. Klicken Sie auf GraphQL und klicken Sie auf den Modus importieren, um unseren Modus in Fauna hochzuladen.
Nach Abschluss des Imports sehen wir, dass Fauna GraphQL -Abfragen und Mutationen erzeugt.
Mögen Sie nicht die automatische Erzeugung von GraphQL? Möchten Sie eine bessere Kontrolle über Ihre Geschäftslogik haben? In diesem Fall können Sie mit Fauna einen benutzerdefinierten GraphQL -Parser definieren. Um mehr zu erfahren, klicken Sie auf diesen Link.
Öffnen wir die Datei pubSpec.yaml und fügen Sie die erforderlichen Abhängigkeiten hinzu.
<code>... dependencies: graphql_flutter: ^4.0.0-beta hive: ^1.3.0 flutter: sdk: flutter ...</code>
Wir haben hier zwei Abhängigkeiten hinzugefügt. GraphQL_FLTERTER ist die GraphQL Client -Bibliothek von Flutter. Es integriert alle modernen Funktionen des GraphQL -Clients in ein einfach zu bedienendes Paket. Wir haben das Hive -Paket auch als Abhängigkeit hinzugefügt. Hive ist eine leichte Schlüsselwertdatenbank, die in Pure Dart für den lokalen Speicher geschrieben wurde. Wir verwenden Hive, um unsere GraphQL -Abfragen zu speichern.
Als nächstes erstellen wir eine neue Datei lib/client_provider.dart. Wir erstellen eine Anbieterklasse in dieser Datei, die unsere Fauna -Konfiguration enthält.
Um eine Verbindung zur GraphQL -API von Fauna herzustellen, müssen wir zunächst einen GraphQLClient erstellen. GraphQLCLIENT benötigt einen Cache und einen Link, um initialisiert zu werden. Schauen wir uns den Code unten an.
<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>
Im obigen Code erstellen wir einen Valuenotifier, um den GraphQLClient zu wickeln. Beachten Sie, dass wir Authlink in den Zeilen 13-15 konfiguriert haben (hervorgehoben). In Zeile 14 haben wir den Administratorschlüssel von Fauna als Teil des Tokens hinzugefügt. Hier habe ich den Administratorschlüssel fest codiert. In Produktionsanwendungen müssen wir jedoch fest codierte Sicherheitsschlüssel von Fauna vermeiden.
Es gibt verschiedene Möglichkeiten, Schlüssel in einer Flutteranwendung zu speichern. Bitte überprüfen Sie diesen Blog als Referenz.
Wir möchten in der Lage sein, Abfragen und Mutation von jedem Widget in der Anwendung aufzurufen. Dazu müssen wir unser Widget mit dem GraphQLProvider -Widget einwickeln.
<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>
Als nächstes gehen wir zur Main.Dart -Datei und wickeln unser Haupt -Widget mit dem ClientProvider -Widget ein. Schauen wir uns den Code unten an.
<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>
Zu diesem Zeitpunkt können alle unsere nachgeschalteten Widgets Abfragen und Mutationenfunktionen ausführen und mit der GraphQL -API interagieren.
Die Demo -Anwendung sollte einfach und leicht zu verstehen sein. Lassen Sie uns ein einfaches Listen -Widget erstellen, das eine Liste aller Rollen anzeigt. Erstellen wir eine neue Datei lib/screens/charakterlist.dart. In dieser Datei werden wir ein neues Widget namens AllCharacters schreiben.
<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>
Wie im obigen Code gezeigt, haben wir eine für Schleife, die die Liste mit einigen gefälschten Daten füllt. Schließlich werden wir GraphQL -Abfragen im Fauna -Backend durchführen und alle Rollen aus der Datenbank erhalten. Bevor wir dies tun, versuchen wir, unsere Bewerbung auszuführen. Wir können unsere Anwendung über den folgenden Befehl ausführen
<code>flutter run</code>
Zu diesem Zeitpunkt sollten wir in der Lage sein, den folgenden Bildschirm zu sehen.
Jetzt haben wir einige grundlegende Widgets, die wir weiterhin zu GraphQL -Abfragen herstellen können. Wir möchten alle Rollen aus der Datenbank anstelle von festcodierten Zeichenfolgen erhalten und sie im AllCharacters -Widget anzeigen.
Kehren wir zu Faunas GraphQL -Spielplatz zurück. Beachten Sie, dass wir die folgende Abfrage ausführen können, um alle Rollen aufzulisten.
<code>query ListAllCharacters { listAllCharacters(_size: 100) { data { _id name description picture } after } }</code>
Um diese Abfrage aus unserem Widget auszuführen, müssen wir einige Änderungen daran vornehmen.
<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>
Zunächst definieren wir die Abfragezeichenfolge, um alle Rollen aus der Datenbank zu erhalten [Zeilen 5 bis 17]. Wir wickeln das List -Widget mit dem Abfrage -Widget in flutter_graphql ein.
Sehen Sie sich die offizielle Dokumentation der Flutter_Graphql -Bibliothek an.
Im Parameter der Abfragemöglichkeiten stellen wir die GraphQL -Abfrage -Zeichenfolge selbst an. Wir können jede schwimmende Punktzahl für den Pollinterval -Parameter übergeben. Das Umfrageintervall definiert, wie lange wir Daten aus dem Backend neutieren möchten. Das Widget hat auch eine Standard -Builder -Funktion. Wir können die Builder-Funktion verwenden, um Abfragergebnisse zu übergeben, Rückruffunktionen wiederzugeben und mehr Rückruffunktionen in den Widget-Baum zu erhalten.
Als nächstes werde ich das Charakter -Widget aktualisieren, um die Zeichendaten auf dem Bildschirm anzuzeigen.
<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>
Wir können unserer Datenbank neue Rollen hinzufügen, indem wir die folgende Mutation ausführen.
<code>mutation CreateNewCharacter($data: CharacterInput!) { createCharacter(data: $data) { _id name description picture } }</code>
Um diese Mutation aus unserem Widget auszuführen, müssen wir das Mutations -Widget aus der Flutter_Graphql -Bibliothek verwenden. Erstellen wir ein neues Widget mit einem einfachen Formular, in dem Benutzer interagieren und Daten eingeben können. Nach der Einreichung des Formulars wird die Createcharacter -Mutation aufgerufen.
<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>
Wie aus dem obigen Code ersichtlich ist, funktioniert das Mutations -Widget sehr ähnlich wie das Abfrage -Widget. Darüber hinaus bietet uns das Mutations -Widget eine OnComplete -Funktion. Diese Funktion gibt das Update -Ergebnis in der Datenbank zurück, nachdem die Mutation abgeschlossen ist.
Wir können Rollen aus der Datenbank löschen, indem wir die Deletecharacter -Mutation ausführen. Wir können diese Mutante zu unserem Charakter hinzufügen und sie auslösen, wenn die Taste gedrückt wird.
<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>
Das Bearbeiten von Daten entspricht dem Hinzufügen und Löschen. Es ist nur eine weitere Mutation in der GraphQL -API. Wir können ein Widget für das Rollenformular für Bearbeitungsformular erstellen, das dem neuen Rollenform -Widget ähnelt. Der einzige Unterschied besteht darin, dass die Bearbeitung des Formulars die UpdateCharacter -Mutation ausführt. Zur Bearbeitung habe ich ein neues Widget Lib/Screens/edit.dart erstellt. Hier ist der Code für dieses Widget.
<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>
Sie können den vollständigen Code dieses Artikels wie folgt anzeigen.
Fragen zu Fauna oder Flattern haben? Sie können mich auf Twitter @Haqueshadid kontaktieren
Github ### Nächste Schritte
Der Hauptzweck dieses Artikels ist es, Sie mit Flutter und Fauna zu beginnen. Wir berühren hier nur die Oberfläche. Das Fauna-Ökosystem bietet Ihrer mobilen Anwendung ein komplettes, automatisches, Entwickler-freundlicher Backend als Service. Wenn Ihr Ziel darin besteht, eine plattformübergreifende mobile App zu veröffentlichen, die für die Produktion in Rekordzeit verwendet werden kann, probieren Sie Fauna und Flattern .
Ich empfehle Ihnen dringend, die offizielle Dokumentations -Website von Fauna zu lesen. Wenn Sie mehr über den GraphQL -Client von Dart/Flutter erfahren möchten, lesen Sie das offizielle Github -Repository von GraphQL_FLTERT.
Ich wünsche Ihnen ein glückliches Programmieren, wir sehen uns beim nächsten Mal.
Das obige ist der detaillierte Inhalt vonSo erstellen Sie eine mobile Full-Stack-Anwendung mit Flutter, Fauna und GraphQL. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!