A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package. Built after react apollo

Overview

Flutter GraphQL

version MIT License All Contributors PRs Welcome

Watch on GitHub Star on GitHub

Table of Contents

About this project

GraphQL brings many benefits, both to the client: devices will need less requests, and therefore reduce data useage. And to the programer: requests are arguable, they have the same structure as the request.

This project combines the benefits of GraphQL with the benefits of Streams in Dart to deliver a high performace client.

The project took inspriation from the Apollo GraphQL client, great work guys!

Note: Still in Beta Docs is coming soon Support for all React Apollo Graphql component supported props is coming soon

Installation

First depend on the library by adding this to your packages pubspec.yaml:

dependencies:
  flutter_graphql: ^1.0.0-rc.3

Now inside your Dart code you can import it.

import 'package:flutter_graphql/flutter_graphql.dart';

Usage

To use the client it first needs to be initialized with an link and cache. For this example we will be uing an HttpLink as our link and InMemoryCache as our cache. If your endpoint requires authentication you can provide some custom headers to HttpLink.

For this example we will use the public GitHub API.

...

import 'package:flutter_graphql/flutter_graphql.dart';

void main() {
  HttpLink link = HttpLink(
    uri: 'https://api.github.com/graphql',
    headers: <String, String>{
      'Authorization': 'Bearer <YOUR_PERSONAL_ACCESS_TOKEN>',
    },
  );

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      cache: InMemoryCache(),
      link: link,
    ),
  );

  ...
}

...

GraphQL Provider

In order to use the client, you Query and Mutation widgets to be wrapped with the GraphQLProvider widget.

We recommend wrapping your MaterialApp with the GraphQLProvider widget.

  ...

  return GraphQLProvider(
    client: client,
    child: MaterialApp(
      title: 'Flutter Demo',
      ...
    ),
  );

  ...

Offline Cache

The in-memory cache can automatically be saved to and restored from offline storage. Setting it up is as easy as wrapping your app with the CacheProvider widget.

It is required to place the CacheProvider widget is inside the GraphQLProvider widget, because GraphQLProvider makes client available trough the build context.

...

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GraphQLProvider(
      client: client,
      child: CacheProvider(
        child: MaterialApp(
          title: 'Flutter Demo',
          ...
        ),
      ),
    );
  }
}

...

Graphql Link and Headers

You can setup authentication headers and other custom links just like you do with Apollo Graphql

  import 'dart:async';

  import 'package:flutter/material.dart';
  import 'package:flutter_graphql/flutter_graphql.dart';
  import 'package:flutter_graphql/src/link/operation.dart';
  import 'package:flutter_graphql/src/link/fetch_result.dart';

  class AuthLink extends Link {
    AuthLink()
        : super(
      request: (Operation operation, [NextLink forward]) {
        StreamController<FetchResult> controller;

        Future<void> onListen() async {
          try {
            var token = await AuthUtil.getToken();
            operation.setContext(<String, Map<String, String>>{
              'headers': <String, String>{'Authorization': '''bearer $token'''}
            });
          } catch (error) {
            controller.addError(error);
          }

          await controller.addStream(forward(operation));
          await controller.close();
        }

        controller = StreamController<FetchResult>(onListen: onListen);

        return controller.stream;
      },
    );
  }

  var cache = InMemoryCache();

  var authLink = AuthLink()
      .concat(HttpLink(uri: 'http://yourgraphqlserver.com/graphql'));
      
  final ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      cache: cache,
      link: authLink,
    ),
  );

  final ValueNotifier<GraphQLClient> anotherClient = ValueNotifier(
    GraphQLClient(
      cache: cache,
      link: authLink,
    ),
  );
    

However note that flutter-graphql does not inject __typename into operations the way apollo does, so if you aren't careful to request them in your query, this normalization scheme is not possible.

Normalization

To enable apollo-like normalization, use a NormalizedInMemoryCache:

ValueNotifier<GraphQLClient> client = ValueNotifier(
  GraphQLClient(
    cache: NormalizedInMemoryCache(
      dataIdFromObject: typenameDataIdFromObject,
    ),
    link: link,
  ),
);

dataIdFromObject is required and has no defaults. Our implementation is similar to apollo's, requiring a function to return a universally unique string or null. The predefined typenameDataIdFromObject we provide is similar to apollo's default:

String typenameDataIdFromObject(Object object) {
  if (object is Map<String, Object> &&
      object.containsKey('__typename') &&
      object.containsKey('id')) {
    return "${object['__typename']}/${object['id']}";
  }
  return null;
}

However note that flutter-graphql does not inject __typename into operations the way apollo does, so if you aren't careful to request them in your query, this normalization scheme is not possible.

Queries

To create a query, you just need to define a String variable like the one below. With full support of fragments

const GET_ALL_PEOPLE = '''
  query getPeople{
    readAll{
      name
      age
      sex
    }
  }
''';

In your widget:

...

Query(
  options: QueryOptions(
    document: GET_ALL_PEOPLE, // this is the query string you just created
    pollInterval: 10,
  ),
  builder: (QueryResult result) {
    if (result.errors != null) {
      return Text(result.errors.toString());
    }

    if (result.loading) {
      return Text('Loading');
    }

    // it can be either Map or List
    List people = result.data['getPeople'];

    return ListView.builder(
      itemCount: people.length,
      itemBuilder: (context, index) {
        final repository = people[index];

        return Text(people['name']);
    });
  },
);

...

Other examples with query argments and passing in a custom graphql client

const READ_BY_ID = '''
  query readById(\$id: String!){
    readById(ID: \$id){
      name
      age
      sex
    }
  }
  
  
final ValueNotifier<GraphQLClient> userClient = ValueNotifier(
  GraphQLClient(
    cache: cache,
    link: authLinkProfile,
  ),
);

''';

In your widget:

...

Query(
  options: QueryOptions(
    document: READ_BY_ID, // this is the query string you just created
    pollInterval: 10,
    client: userClient.value
  ),
  builder: (QueryResult result) {
    if (result.errors != null) {
      return Text(result.errors.toString());
    }

    if (result.loading) {
      return Text('Loading');
    }

    // it can be either Map or List
    List person = result.data['getPeople'];

    return Text(person['name']);
  },
);

...

Mutations

Again first create a mutation string:

const LIKE_BLOG = '''
  mutation likeBlog(\$id: Int!) {
    likeBlog(id: \$id){
      name
      author {
        name
        displayImage
      }
  }
''';

The syntax for mutations is fairly similar to that of a query. The only diffence is that the first argument of the builder function is a mutation function. Just call it to trigger the mutations (Yeah we deliberately stole this from react-apollo.)

...

Mutation(
  options: MutationOptions(
    document: LIKE_BLOG, // this is the mutation string you just created
  ),
  builder: (
    RunMutation runMutation,
    QueryResult result,
  ) {
    return FloatingActionButton(
      onPressed: () => runMutation({
        'id': <BLOG_ID>,
      }),
      tooltip: 'Star',
      child: Icon(Icons.star),
    );
  },
);

...

Subscriptions (Experimental)

The syntax for subscriptions is again similar to a query, however, this utilizes WebSockets and dart Streams to provide real-time updates from a server. Before subscriptions can be performed a global intance of socketClient needs to be initialized.

We are working on moving this into the same GraphQLProvider stucture as the http client. Therefore this api might change in the near future.

socketClient = await SocketClient.connect('ws://coolserver.com/graphql');

Once the socketClient has been initialized it can be used by the Subscription Widget

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Subscription(
          operationName,
          query,
          variables: variables,
          builder: ({
            bool loading,
            dynamic payload,
            dynamic error,
          }) {
            if (payload != null) {
              return Text(payload['requestSubscription']['requestData']);
            } else {
              return Text('Data not found');
            }
          }
        ),
      )
    );
  }
}

Graphql Consumer

You can always access the client direcly from the GraphQLProvider but to make it even easier you can also use the GraphQLConsumer widget. You can also pass in a another client to the consumer

  ...

  return GraphQLConsumer(
    builder: (GraphQLClient client) {
      // do something with the client

      return Container(
        child: Text('Hello world'),
      );
    },
  );

  ...

A different client:

  ...

  return GraphQLConsumer(
    client: userClient,
    builder: (GraphQLClient client) {
      // do something with the client

      return Container(
        child: Text('Hello world'),
      );
    },
  );

  ...

Fragments

There is support for fragments and it's basically how you use it in Apollo React. For example define your fragment as a dart String.

  ...
const UserFragment = '''
  fragment UserFragmentFull on Profile {
    address {
      city
      country
      postalCode
      street
    }
    birthdate
    email
    firstname
    id
    lastname
  }
  ''';

  ...

Now you can use it in your Graphql Query or Mutation String like below

  ...

  const CURRENT_USER = '''
    query read{
      read {
      ...UserFragmentFull
      }
    }
    $UserFragment
  ''';

  ...

or

  ...

  const GET_BLOGS = '''
    query getBlogs{
      getBlog {
        title
        description
        tags
        
        author {
          ...UserFragmentFull
        }
    }
    $UserFragment
  ''';

  ...

Outside a Widget

Similar to withApollo or graphql HoC that passes the client to the component in react, you can call a graphql query from any part of your code base even in a your service class or in your Scoped MOdel or Bloc class. Example

  ...

  class AuthUtil{
    static Future<String> getToken() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      return await prefs.getString('token');
    }

    static Future setToken(value) async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      return await prefs.setString('token', value);
    }

    static removeToken() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      return await prefs.remove('token');
    }

    static clear() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      return await prefs.clear();
    }
    
    static Future<bool> logIn(String username, String password) async {
      var token;

      QueryOptions queryOptions = QueryOptions(
          document: LOGIN,
          variables: {
            'username': username,
            'password': password
          }
      );

      if (result != null) {
        this.setToken(result);
        return clientProfile.value.query(queryOptions).then((result) async {

          if(result.data != null) {
            token = result.data['login']['token];
            notifyListeners();
            return token;
          } else {
            return throw Error;
          }

        }).catchError((error) {
            return throw Error;
        });
      } else
        return false;
    }
  }

  ...

In a scoped model:

  ...
class AppModel extends Model {

  String token = '';
  var currentUser = new Map <String, dynamic>();

  static AppModel of(BuildContext context) =>
      ScopedModel.of<AppModel>(context);

  void setToken(String value) {
    token = value;
    AuthUtil.setAppURI(value);
    notifyListeners();
  }


  String getToken() {
    if (token != null) return token;
    else AuthUtil.getToken();
  }

  getCurrentUser() {
    return currentUser;
  }

  Future<bool> isLoggedIn() async {

    var result = await AuthUtil.getToken();
    print(result);

    QueryOptions queryOptions = QueryOptions(
        document: CURRENT_USER
    );

    if (result != null) {
      print(result);
      this.setToken(result);
      return clientProfile.value.query(queryOptions).then((result) async {

        if(result.data != null) {
          currentUser = result.data['read'];
          notifyListeners();
          return true;
        } else {
          return false;
        }

      }).catchError((error) {
        print('''Error => $error''');
        return false;
      });
    } else
      return false;
  }
}

Roadmap

This is currently our roadmap, please feel free to request additions/changes.

Feature Progress
Queries
Mutations
Subscriptions
Query polling
In memory cache
Offline cache sync
Optimistic results 🔜
Client state management 🔜
Modularity 🔜
Documentation 🔜

Contributing

Feel free to open a PR with any suggestions! We'll be actively working on the library ourselves. If you need control to the repo, please contact me Rex Raphael. Please fork and send your PRs to the v.1.0.0 branch.

This project follows the all-contributors specification. Contributions of any kind are welcome!

Comments
  • WANT TO BE A CORE CONTIBUTOR

    WANT TO BE A CORE CONTIBUTOR

    Do you want to be a core contributor, please drop your names so we can discuss the way forward for the library. We need a very stable GraphQL library for Flutter.

    opened by juicycleff 4
  • Relies on an older version of path_provider

    Relies on an older version of path_provider

    Would it be possible to update the path_provider dependency to the latest version, i am getting incompatible since it requires version ^0.4.1 and using Cached Network Image which is on version ^0.5.0+1.

    Because cached_network_image >=0.7.0 depends on flutter_cache_manager ^0.3.2 which depends on path_provider ^0.5.0+1, cached_network_image >=0.7.0 requires path_provider ^0.5.0+1.
    
    And because flutter_graphql 1.0.0-rc.3 depends on path_provider ^0.4.1 and no versions of flutter_graphql match >1.0.0-rc.3 <2.0.0, cached_network_image >=0.7.0 is incompatible with flutter_graphql ^1.0.0-rc.3.
    
    opened by mainawycliffe 2
  • Loading boolean only set true on initial load

    Loading boolean only set true on initial load

    Describe the bug The loading boolean is only ever true, once. Meaning, if you were to wrap the Query in a StatefulWidget, or a ScopedModelDescendant as in my case, the build method causes the Query to fire off its builder, but the result.loading is never actually set to true to indicate that the Query is loading again.

    To Reproduce Steps to reproduce the behavior:

    1. Construct a StatefulWidget or ScopedModelDescedant, or anything that will fire off a rebuild
    2. Set a Query widget as a child of your StatefulWidget
    3. Create an if statement within the builder for Query to print out "Loading!" when result.loading is true
    4. Add a button or some other trigger that will change the state of the Queries parent, firing the parent's build method
    5. Load the application
    6. Notice that "Loading!" fires on first application load
    7. Press the button / fire the trigger
    8. Notice that "Loading!" does not appear in the log, indicating that result.loading is never reset to true

    Expected behavior This might be my misunderstanding when it comes to the loading boolean, but I anticipated that loading would be set to true whenever the Query is fetching data, and especially when the whole widget is rebuilding.

    Screenshots n/a

    Desktop (please complete the following information):

    • OS: Windows
    • Version 10

    Smartphone (please complete the following information):

    • Device: Genymotion Emulator
    • OS: Android Pie
    • Version 1.0.0-rc.3

    Additional context I've done some digging around in the query.dart file and I've found that everything appears to initialize as expected when the dependencies change, and the Query widget's internal build method does in fact fire every time we run its StatefulWidget parent's build method, which does in fact set initialData: QueryResult { loading: true } (line 88). My best guess is that this has something to do with not fully closing the stream, or perhaps something to do with the controller? No idea!

    opened by Smidds 2
  • Update path_provider to latest version

    Update path_provider to latest version

    There is a conflict happening across some popular libraries in regards to this path_provider dependency. This is in regards to Android X compatibility and migration from deprecated Android Support Library

    Describe the purpose of the pull request.

    Fixes / Enhancements

    • Updated path_provider dependency to the latest version
    opened by leoafarias 2
  • Question: Purpose of

    Question: Purpose of "getOperationName" function

    While looking through the source of this package, I found this comment: https://github.com/snowballdigital/flutter-graphql/blob/master/lib/src/core/query_manager.dart#L67-L68:

    // XXX there is a bug in the `graphql_parser` package, where this result might be
    // null event though the operation name is present in the document
    

    I am the creator/maintainer of the graphql_parser package. Can you provide some details about the bug you are referring to?

    In a query like this:

    {
      foo,
      bar: baz
      ...quux
      ... on A {b, c}
    }
    

    There is no operation name, and this a pretty common case.

    Is it possible that the query you were using simply didn't have an operation name? Otherwise, if you can provide a repro, I can fix it.

    opened by thosakwe 1
  • Problem with subscription

    Problem with subscription

    Hello, I have a problem with Graphql subscription. When I try with graphql playground - all works fine: on the server I receive an answer about subscription, but when I trying to connect in flutter - I'm getting only loading true on the client, but nothing on the server.

    import 'package:flutter/material.dart';
    import 'package:flutter_graphql/flutter_graphql.dart';
    
    void main() async {
      socketClient = await SocketClient.connect('ws://address',
        headers: {
          'authorization': "accessTokenHere"
        }
      );
    
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        final title = 'WebSocket Demo';
        return MaterialApp( 
          title: title,
          home: MyHomePage(
            title: title,
          ),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      final String title;
    
      MyHomePage({Key key, @required this.title})
          : super(key: key);
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      TextEditingController _controller = TextEditingController();
    
      static String operationName = "notification";
      String query = """subscription $operationName{
        notification{
          messageCount
        }
      }""".replaceAll('\n', ' ');
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Form(
                  child: TextFormField(
                    controller: _controller,
                    decoration: InputDecoration(labelText: 'Send a message'),
                  ),
                ),
                Subscription(
                  operationName,
                  query,
                  variables: {},
                  builder: ({
                    bool loading,
                    dynamic payload,
                    dynamic error,
                  }) {
                    print(loading);
                    print(payload);
                    print(error);
                    if (payload != null) {
                      return Text(payload['requestSubscription']['requestData']);
                    } else {
                      return Text('Data not found');
                    }
                  }
                ),
              ],
            ),
          ),
        );
      }
    
    }
    
    opened by Goolpe 1
  • why not transfer ownership?

    why not transfer ownership?

    @juicycleff you can transfer repository ownership, which I think would be less confusing than copy-pasting for end-users, and maintain the github repo-relationship graph Screen Shot 2019-03-19 at 18 52 25

    opened by micimize 1
  • Export classes needed to setup authentication headers

    Export classes needed to setup authentication headers

    Fixes / Enhancements

    The README states that, in order to inject authentication headers, we need to import these files:

    import 'package:flutter_graphql/flutter_graphql.dart';
    import 'package:flutter_graphql/src/link/operation.dart';
    import 'package:flutter_graphql/src/link/fetch_result.dart';
    

    Importing files that are inside the src folder of a lib is discouraged.

    This can be fixed by exporting those 2 files in the flutter_graphql.dart, as proposed here.

    opened by ianko 1
  • Set loading to true on rebuild

    Set loading to true on rebuild

    This PR makes it possible for the loading boolean to be set to true when the Query widget is rebuilt.

    Fixes / Enhancements

    • Fixes #6 where result.loading within the QueryResult, it used to not set to true when the Query widget was rebuilt.
    opened by Smidds 0
  • subscription foreground

    subscription foreground

    Is your feature request related to a problem? Please describe. now I can't send a request in the background, as well as send and subscribe

    Describe the solution you'd like i don't no :(

    Describe alternatives you've considered i propabbly use this package https://pub.dev/packages/workmanager

    Additional context Add any other context or screenshots about the feature request here.

    opened by timeshift92 0
  • fixes function_typed_parameter_var

    fixes function_typed_parameter_var

    On Flutter 1.5.4 (current stable), this is breaking with the following message:

    Function-typed parameters can't specify 'const', 'final' or 'var' in place of a return type.
    Try replacing the keyword with a return type.dart(function_typed_parameter_var)
    

    Breaking changes

    NONE

    Fixes / Enhancements

    • Fixed by removing the final keyword.
    opened by ianko 0
  • Re-sending a failed query

    Re-sending a failed query

    I've integrated the Query widget into my app, and it works just fine the first time it runs. However, I'd like to have a "try again" button if the query fails that would re-send the query. However, I can't figure out how to refresh/re-send a query. I've written the following code so far, and every time I hit "try again", I can see Built event widget tree printed to my console, but the query itself isn't re-sent:

    @override Widget build(BuildContext context) {
      print("Built event widget tree");
      const request = '''
      {
        festapp(id:"931") {
          events {
            name
            startsAt
            image
          }
        }
      }
      ''';
      return Query(
          options: QueryOptions(
            document: request,
            fetchPolicy: FetchPolicy.noCache,
            errorPolicy: ErrorPolicy.all,
            pollInterval: 5
          ),
          builder: (QueryResult result) {
            if (result.errors != null) {
              return Center(child:
                Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text(result.errors.toString()),
                    CupertinoButton.filled(
                      child: Text("Try again"),
                      onPressed: (){
                        setState((){});
                      },
                    )
                  ],
                )
              );
            }
    
            if (result.loading) {
              return Center(
                child: CupertinoActivityIndicator(
                  radius: 20,
                  animating: true,
                ),
              );
            }
    
            List events = result.data["festapp"]["events"];
    
            return ListView.separated(itemBuilder: (context, row) => EventCell(Event.fromAPIData(events[row])),
                separatorBuilder: (context, row) {
                  return Container(
                    height: 1,
                    color: Color.fromARGB(20, 0, 0, 0),
                    margin: EdgeInsets.only(left: 8),
                  );
                },
                itemCount: events.length);
            }
        );
      }
    opened by raysarebest 5
  • Comparison with https://github.com/zino-app/graphql-flutter

    Comparison with https://github.com/zino-app/graphql-flutter

    Hi, this package was originally a fork of https://github.com/zino-app/graphql-flutter: https://github.com/juicycleff/flutter-graphql Then it transferred to another namespace (snowballdigital) and it lost its reference as a fork of zino-app.

    Could you please describe how this package differs from the original package that is still being maintained and what design decisions made you decide to go a different direction (and not try to steer to a single effort, maintained and contributed by more devs?)

    We as a team are also willing to contribute, but must decide first what package is the most promising to end up as the project with most traction and community support.

    Thanks!

    opened by Sewdn 3
  • pollInterval should not poll the server when set to zero

    pollInterval should not poll the server when set to zero

    Describe the bug When pollInterval is set to zero, it constantly polls to the server, without stopping. Since it follows Apollo GrapphQL patterns, this should disable polling altogether.

    To Reproduce

    Set pollInterval to 0 at QueryOptions

    Expected behavior Should disable polling altogether

    opened by mainawycliffe 2
Owner
Snowball Digital
At Snowball we love product design and engineering
Snowball Digital
Flutter After Layout - Brings functionality to execute code after the first layout of a widget has been performed

Flutter After Layout - Brings functionality to execute code after the first layout of a widget has been performed, i.e. after the first frame has been displayed. Maintainer: @slightfoot

Flutter Community 432 Jan 3, 2023
A demo Project Showcasing HowTo use GraphQL to conect as a client to a GraphQL service.

graphql_demoapp A Flutter App to demonstrate how to work with GraphQL Server, connecting through our app as a client. Working with basic queries and m

Igor L Sambo 2 Nov 7, 2021
This is a JazzCash UI clone ( Modern Wallet App in Pakistan), implementing modern app bar animmation. One can take a concept of making app bar with animation.

jazzcash_ui This is a JazzCash UI clone ( Modern Wallet App in Pakistan), implementing modern app bar animmation. One can take a concept of making app

null 9 Nov 27, 2022
Lite-graphql - A light way implementation of GraphQL client in dart language

lite GraphQL client A light way implementation of GraphQL client in dart languag

Vincenzo Palazzo 3 Mar 17, 2022
Trying out Flutter for desktop Web app development as an alternative to SPA frameworks (such as React and Angular) by recreating one of the pages of an existing CV Management web app

HTML Renderer Demo CanvasKit Renderer Demo Reddit discussion This repo contains a PoC of using Flutter as a traditional SPA framework for creating a d

Maxim Saplin 20 Oct 11, 2022
A package that gives us a modern way to show animated border as a placeholder while loading our widget with easy customization and ready to use.

A package that gives us a modern way to show animated border as a placeholder while loading our widget with easy customization and ready to use.

Mohit Chauhan 8 Oct 3, 2022
Bringing back contex.read and context.watch for riverpod

This package brings back the context extensions for riverpod that were discontinued in version 1.0.0. To read any provider, do context.read(myProvider

Kilian Schulte 2 Sep 28, 2022
Material color picker, you can customize colors. Selection in two step, first main color and after shades.

Flutter Material Color Picker Material Color picker is a Flutter widget, that can be customizable. By default, it's Material Colors, but you can defin

Jean-Charles Moussé 70 Nov 25, 2022
I just designed this within 30 min after watching a video by tech school on YouTube.

nft_app A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if t

ogunmolu oluwaseun 0 Dec 25, 2021
[Flutter SDK V.2] - Youtube Video is a Flutter application built to demonstrate the use of Modern development tools with best practices implementation like Clean Architecture, Modularization, Dependency Injection, BLoC, etc.

[Flutter SDK V.2] - Youtube Video is a Flutter application built to demonstrate the use of Modern development tools with best practices implementation like Clean Architecture, Modularization, Dependency Injection, BLoC, etc.

R. Rifa Fauzi Komara 17 Jan 2, 2023
Raden Saleh 20 Aug 12, 2023
Raden Saleh 53 Jul 27, 2023
Flutter React Golang WebRTC等技术学习笔记

study-notes Flutter React Golang WebRTC等技术学习笔记 学习资源 除了一些文章外,亢老师还精心录制了一些视频教程供大家学习. 国内用户请查看核心系列课程(国内). 海外华人用户请查看核心系列课程(海外), 支持paypal支付.可放心购买 核心系列课程(国内)

亢少军 15 Nov 11, 2022
Flutter's [FlatList] widget for React Native friendly developers

FlatList for Flutter [FlatList] widget in Flutter which will be familiar to React Native developers. Motivation While there are opinionated ways to bu

Hyo 5 Dec 21, 2022
react-native native module for In App Purchase.

Documentation Published in website. Announcement Version 8.0.0 is currently in release candidate. The module is completely rewritten with Kotlin and S

dooboolab 2.3k Dec 31, 2022
This perfect starter kit is an app based on React Native and UI Kitten library with Light and Dark themes support.

Kitten Tricks This perfect starter kit is an app based on React Native and UI Kitten library with Light and Dark themes support. It’s completely free

Akveo 7k Dec 30, 2022
An all-in-one Fllutter package for state management, reactive objects, animations, effects, timed widgets etc.

Frideos An all-in-one package for state management, streams and BLoC pattern, animations and timed widgets, effects. Contents 1. State management Gett

Francesco Mineo 188 Dec 23, 2022
An online learning application with all needed features implemented

ELEARN An online learning application: completely functional with payment systems and firebase added. Overview This application has been developed to

null 1 Nov 4, 2021