Wie man eine Full-Stack-Mobile-Anwendung mit Flutter, Fauna und GraphQL erstellt

Avatar of Shadid Haque
Shadid Haque am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Flutter ist das UI-Framework von Google, das zur Erstellung flexibler, ausdrucksstarker plattformübergreifender mobiler Anwendungen verwendet wird. Es ist eines der am schnellsten wachsenden Frameworks für die Entwicklung mobiler Apps. Fauna hingegen ist eine transaktionale, entwicklerfreundliche serverlose Datenbank, die natives GraphQL unterstützt. Flutter + Fauna ist eine himmlische Kombination. Wenn Sie eine funktionsreiche Full-Stack-Anwendung in Rekordzeit erstellen und ausliefern möchten, sind Flutter und Fauna das richtige Werkzeug für den Job. In diesem Artikel führen wir Sie durch den Aufbau Ihrer allerersten Flutter-Anwendung mit einem Fauna- und GraphQL-Backend.

Den vollständigen Code für diesen Artikel finden Sie auf GitHub.

Lernziel

Am Ende dieses Artikels sollten Sie wissen, wie Sie

  1. eine Fauna-Instanz einrichten,
  2. ein GraphQL-Schema für Fauna erstellen,
  3. einen GraphQL-Client in einer Flutter-App einrichten und
  4. Abfragen und Mutationen gegen das Fauna-GraphQL-Backend ausführen.

Fauna vs. AWS Amplify vs. Firebase: Welche Probleme löst Fauna? Wie unterscheidet sie sich von anderen serverlosen Lösungen? Wenn Sie neu bei Fauna sind und mehr darüber erfahren möchten, wie Fauna im Vergleich zu anderen Lösungen abschneidet, empfehle ich Ihnen, diesen Artikel zu lesen.

Was bauen wir?

Wir werden eine einfache mobile Anwendung erstellen, mit der Benutzer ihre Lieblingscharaktere aus Filmen und Serien hinzufügen, löschen und aktualisieren können.

Fauna einrichten

Gehen Sie zu fauna.com und erstellen Sie ein neues Konto. Sobald Sie angemeldet sind, können Sie eine neue Datenbank erstellen.

Geben Sie Ihrer Datenbank einen Namen. Ich werde meine flutter_demo nennen. Als Nächstes können wir eine Regionengruppe auswählen. Für diese Demo wählen wir klassisch. Fauna ist eine global verteilte serverlose Datenbank. Es ist die einzige Datenbank, die schnellen Lese- und Schreibzugriff von überall unterstützt. Stellen Sie es sich wie ein CDN (Content Delivery Network) für Ihre Datenbank vor. Um mehr über Regionengruppen zu erfahren, folgen Sie dieser Anleitung.

Generieren eines Admin-Schlüssels

Sobald die Datenbank erstellt ist, gehen Sie zum Sicherheitstab. Klicken Sie auf die Schaltfläche "Neuer Schlüssel" und erstellen Sie einen neuen Schlüssel für Ihre Datenbank. Bewahren Sie diesen Schlüssel sicher auf, da wir ihn für unsere GraphQL-Operationen benötigen.

Wir erstellen einen Admin-Schlüssel für unsere Datenbank. Schlüssel mit der Rolle "Admin" werden zur Verwaltung ihrer zugehörigen Datenbank verwendet, einschließlich der Datenbankzugriffsanbieter, Kinddatenbanken, Dokumente, Funktionen, Indizes, Schlüssel, Token und benutzerdefinierter Rollen. Weitere Informationen zu den verschiedenen Sicherheitsschlüsseln und Zugriffsberechtigungen von Fauna finden Sie unter dem folgenden Link.

Ein GraphQL-Schema erstellen

Wir werden eine einfache App erstellen, mit der Benutzer ihre Lieblings-TV-Charaktere hinzufügen, aktualisieren und löschen können.

Neues Flutter-Projekt erstellen

Erstellen wir ein neues Flutter-Projekt, indem wir die folgenden Befehle ausführen.

flutter create my_app

Innerhalb des Projektverzeichnisses erstellen wir eine neue Datei namens graphql/schema.graphql.

In der Schemadatei definieren wir die Struktur unserer Sammlung. Sammlungen in Fauna sind Tabellen in SQL ähnlich. Wir brauchen vorerst nur eine Sammlung. Wir nennen sie Character.

### schema.graphql
type Character {
    name: String!
    description: String!
    picture: String
}
type Query {
    listAllCharacters: [Character]
}

Wie oben zu sehen ist, haben wir einen Typ namens Character mit mehreren Eigenschaften (d. h. name, description, picture usw.) definiert. Betrachten Sie Eigenschaften als Spalten einer SQL-Datenbank oder als Schlüssel-Wert-Paare einer NoSQL-Datenbank. Wir haben auch eine Abfrage definiert. Diese Abfrage gibt eine Liste der Charaktere zurück.

Gehen wir nun zurück zum Fauna-Dashboard. Klicken Sie auf GraphQL und dann auf "Schema importieren", um unser Schema nach Fauna hochzuladen.

Nach Abschluss des Imports sehen wir, dass Fauna die GraphQL-Abfragen und Mutationen generiert hat.

Sie mögen kein automatisch generiertes GraphQL? Möchten Sie mehr Kontrolle über Ihre Geschäftslogik? In diesem Fall können Sie mit Fauna Ihre eigenen benutzerdefinierten GraphQL-Resolver definieren. Um mehr zu erfahren, folgen Sie diesem Link.

GraphQL-Client in der Flutter-App einrichten

Öffnen wir unsere Datei pubspec.yaml und fügen Sie die erforderlichen Abhängigkeiten hinzu.

...
dependencies:
  graphql_flutter: ^4.0.0-beta
  hive: ^1.3.0
  flutter:
    sdk: flutter
...

Wir haben hier zwei Abhängigkeiten hinzugefügt. graphql_flutter ist eine GraphQL-Client-Bibliothek für Flutter. Sie vereint alle modernen Funktionen von GraphQL-Clients in einem einfach zu bedienenden Paket. Wir haben auch das hive-Paket als Abhängigkeit hinzugefügt. Hive ist eine leichte Schlüssel-Wert-Datenbank, die in reinem Dart für lokalen Speicher geschrieben wurde. Wir verwenden Hive, um unsere GraphQL-Abfragen zu cachen.

Als Nächstes erstellen wir eine neue Datei lib/client_provider.dart. In dieser Datei erstellen wir eine Anbieterklasse, die unsere Fauna-Konfiguration enthält.

Um eine Verbindung zur GraphQL-API von Fauna herzustellen, müssen wir zuerst einen GraphQLClient erstellen. Ein GraphQLClient benötigt einen Cache und einen Link zur Initialisierung. Werfen wir einen Blick auf den folgenden 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,
    ),
  );
} 

Im obigen Code haben wir ein ValueNotifier erstellt, um den GraphQLClient zu umschließen. Beachten Sie, dass wir den AuthLink in den Zeilen 13–15 (hervorgehoben) konfiguriert haben. In Zeile 14 haben wir den Admin-Schlüssel von Fauna als Teil des Tokens hinzugefügt. Hier habe ich den Admin-Schlüssel fest codiert. In einer Produktionsanwendung müssen wir es jedoch vermeiden, Sicherheitschlüssel von Fauna fest zu codieren.

Es gibt mehrere Möglichkeiten, Geheimnisse in einer Flutter-Anwendung zu speichern. Bitte werfen Sie einen Blick auf diesen Blogbeitrag als Referenz.

Wir möchten Query und Mutation von jedem Widget unserer Anwendung aus aufrufen können. Dazu müssen wir unsere Widgets mit dem GraphQLProvider-Widget umschließen.

// lib/client_provider.dart

....

/// Wraps the root application with the `graphql_flutter` client.
/// We use the cache for all state management.
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,
    );
  }
}

Als Nächstes gehen wir zu unserer Datei main.dart und umschließen unser Hauptwidget mit dem ClientProvider-Widget. Werfen wir einen Blick auf den folgenden 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(),
        }
      ),
    );
  }
}

An diesem Punkt haben alle unsere nachfolgenden Widgets Zugriff auf die Funktionen Queries und Mutations und können mit der GraphQL-API interagieren.

Anwendungsseiten

Demo-Anwendungen sollten einfach und leicht verständlich sein. Erstellen wir ein einfaches Listen-Widget, das die Liste aller Charaktere anzeigt. Erstellen wir eine neue Datei lib/screens/character-list.dart. In dieser Datei schreiben wir ein neues Widget namens AllCharacters.

// 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 < 10; i++) 
                      CharacterTile()
                  ],
                )
            ])
          )
        ],
      ),
    );
  }
}

// Character-tile.dart
class CharacterTile extends StatefulWidget {
  CharacterTilee({Key key}) : super(key: key);
  @override
  _CharacterTileState createState() => _CharacterTileeState();
}
class _CharacterTileState extends State<CharacterTile> {
  @override
  Widget build(BuildContext context) {
    return Container(
       child: Text(&quot;Character Tile&quot;),
    );
  }
}

Wie im obigen Code zu sehen ist, haben wir [Zeile 37] eine for-Schleife, um die Liste mit einigen Dummy-Daten zu füllen. Schließlich werden wir eine GraphQL-Abfrage an unser Fauna-Backend stellen und alle Charaktere aus der Datenbank abrufen. Bevor wir dies tun, versuchen wir, unsere Anwendung so auszuführen, wie sie ist. Wir können unsere Anwendung mit dem folgenden Befehl ausführen

flutter run

An diesem Punkt sollten wir den folgenden Bildschirm sehen können.

Abfragen und Mutationen ausführen

Da wir nun einige grundlegende Widgets haben, können wir mit dem Einrichten von GraphQL-Abfragen fortfahren. Anstatt fest codierte Zeichenketten zu verwenden, möchten wir alle Charaktere aus unserer Datenbank abrufen und sie im Widget AllCharacters anzeigen.

Gehen wir zurück zum GraphQL-Playground von Fauna. Beachten Sie, dass wir die folgende Abfrage ausführen können, um alle Charaktere aufzulisten.

query ListAllCharacters {
  listAllCharacters(_size: 100) {
    data {
      _id
      name
      description
      picture
    }
    after
  }
}

Um diese Abfrage von unserem Widget aus auszuführen, müssen wir einige Änderungen daran vornehmen.

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 query we want to perform
                pollInterval: Duration(seconds: 120), // refetch interval
              ), 
              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),
                  ],
                );
              })
            ])
          )
        ],
      ),
    );
  }
} 

Zuallererst haben wir die Abfragezeichenfolge zum Abrufen aller Charaktere aus der Datenbank definiert [Zeile 5 bis 17]. Wir haben unser Listen-Widget mit einem Query-Widget von flutter_graphql umschlossen.

Werfen Sie gerne einen Blick in die offizielle Dokumentation für die flutter_graphql-Bibliothek.

Im Argument für die Abfrageoptionen stellen wir die GraphQL-Abfragezeichenfolge selbst bereit. Wir können für das Argument pollInterval jede Gleitkommazahl übergeben. Poll Interval definiert, wie oft wir Daten von unserem Backend neu abrufen möchten. Das Widget verfügt außerdem über eine Standard-Builder-Funktion. Wir können eine Builder-Funktion verwenden, um das Abfrageergebnis, die Refetch-Callback-Funktion und die Fetch-More-Callback-Funktion an den Widget-Baum weiterzugeben.

Als Nächstes werde ich das Widget CharacterTile aktualisieren, um die Charakterdaten auf dem Bildschirm anzuzeigen.

// 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,
                  ),
                ],
              )
            )
          ],
        ),
      ),
    );
  }
}

Hinzufügen neuer Daten

Wir können neue Charaktere zu unserer Datenbank hinzufügen, indem wir die unten stehende Mutation ausführen.

mutation CreateNewCharacter($data: CharacterInput!) {
    createCharacter(data: $data) {
      _id
      name
      description
      picture
    }
}

Um diese Mutation von unserem Widget aus auszuführen, können wir das Mutation-Widget aus der flutter_graphql-Bibliothek verwenden. Erstellen wir ein neues Widget mit einem einfachen Formular, mit dem die Benutzer interagieren und Daten eingeben können. Sobald das Formular abgesendet wurde, wird die Mutation createCharacter aufgerufen.

// 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,
                        }
                      });
                    },
                  ),
                );
              }
            )
          ],
        ),
      ),
    );
  }
}

Wie aus dem obigen Code ersichtlich ist, funktioniert das Mutation-Widget sehr ähnlich wie das Query-Widget. Zusätzlich bietet uns das Mutation-Widget eine onComplete-Funktion. Diese Funktion gibt das aktualisierte Ergebnis aus der Datenbank zurück, nachdem die Mutation abgeschlossen ist.

Löschen von Daten

Um einen Charakter aus unserer Datenbank zu löschen, können wir die Mutation deleteCharacter ausführen. Wir können diese Mutationsfunktion zu unserem CharacterTile hinzufügen und sie auslösen, wenn ein Button gedrückt wird.

// 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,
                  ),
                ],
              )
            )
          ],
        ),
      ),
    );
  }
}

Bearbeiten von Daten

Das Bearbeiten von Daten funktioniert genauso wie das Hinzufügen und Löschen. Es ist nur eine weitere Mutation in der GraphQL-API. Wir können ein Widget mit einem Formular zum Bearbeiten von Charakteren erstellen, ähnlich wie das Widget für neue Charaktere. Der einzige Unterschied besteht darin, dass das Bearbeitungsformular die Mutation updateCharacter ausführt. Zum Bearbeiten habe ich ein neues Widget lib/screens/edit.dart erstellt. Hier ist der Code für dieses Widget.

// 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'],
                      });
                    },
                  ),
                );
              }
            ),
           ]
         )
       ),
    );
  }
}

Sie können den vollständigen Code für diesen Artikel unten einsehen.

Haben Sie Fragen zu Fauna oder Flutter? Sie können mich auf Twitter unter @HaqueShadid erreichen.

Wie geht es weiter?

Die Hauptabsicht dieses Artikels ist es, Sie mit Flutter und Fauna vertraut zu machen. Wir haben hier nur an der Oberfläche gekratzt. Das Fauna-Ökosystem bietet ein komplettes, automatisch skalierendes, entwicklerfreundliches Backend als Service für Ihre mobilen Anwendungen. Wenn es Ihr Ziel ist, eine produktionsreife plattformübergreifende mobile Anwendung in Rekordzeit auszuliefern, dann probieren Sie Fauna und Flutter aus.

Ich empfehle dringend, die offizielle Dokumentationsseite von Fauna zu lesen. Wenn Sie mehr über GraphQL-Clients für Dart/Flutter erfahren möchten, schauen Sie sich das offizielle GitHub-Repository für graphql_flutter an.

Viel Spaß beim Hacking und bis zum nächsten Mal.