async_resource_flutter 0.1.1

async_resource_flutter #

Flutter plugin for async_resource. Automatically cache network resources and use them when offline.

See https://pub.dartlang.org/packages/async_resource.

Examples #

SharedPrefsResource examples #

final stringRes = StringPrefsResource('string');
final boolRes = BoolPrefsResource('bool');
final intRes = IntPrefsResource('int');
final doubleRes = DoublePrefsResource('double');
final stringListRes = StringListPrefsResource('list');

Flutter examples #

This is taken from the working example.

import 'package:flutter/material.dart';
import 'package:async_resource_flutter/async_resource_flutter.dart';
// import 'package:async_resource_example/config.dart';
import 'src/resources.dart';


Color textColor = Colors.black;

void main() => runApp(MaterialApp(
    title: appName,
    home: FutureHandler(
      // This looks up the application documents directory so network requests
      // know where to cache results.
      future: MobileResources.init(),
      handler: (context, resources) => HomePage(),
    )));

class HomePage extends StatefulWidget {
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  static const lightBg = Color(0xFFfefefe);
  static const darkBg = Color(0xFF333333);

  int selectedPage = 0;
  Color bgColor = lightBg;

  @override
  void initState() {
    super.initState();
    resources.darkBackground.get().then((isDark) => setState(() {
          bgColor = isDark ? darkBg : lightBg;
          textColor = isDark ? Colors.white : Colors.black;
        }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: bgColor,
      appBar: AppBar(
        title: Text(appName),
        actions: <Widget>[
          Switch(value: bgColor == darkBg, onChanged: (_) => _toggleColor())
        ],
      ),
      body: _buildBody(context),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        currentIndex: selectedPage,
        onTap: (index) {
          if (index != selectedPage) setState(() => selectedPage = index);
        },
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.ac_unit), title: Text('Streamed\nHelpers')),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings_remote),
              title: Text('Future\nHelpers')),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings_input_antenna),
              title: Text('Streamed')),
          BottomNavigationBarItem(
              icon: Icon(Icons.shuffle), title: Text('Future')),
        ],
      ),
    );
  }

  Widget _buildBody(BuildContext context) {
    switch (selectedPage) {
      case 1:
        return PostsWithHelpers();
      case 2:
        return StreamedPostsWithoutHelpers();
      case 3:
        return PostsWithoutHelpers();
      case 0:
      default:
        return StreamedPostsWithHelpers();
    }
  }

  void _toggleColor() => setState(() {
        final turningDark = bgColor == lightBg;
        bgColor = turningDark ? darkBg : lightBg;
        textColor = turningDark ? Colors.white : Colors.black;
        resources.darkBackground.write(turningDark);
      });
}

//
// Different ways of building ListTiles from a `Post`.
//

Widget _buildPost(Post post) => ListTile(
      title: Text('${post?.title}', style: TextStyle(color: textColor)),
      subtitle: Text('${post?.body}', style: TextStyle(color: textColor)),
      leading: Text('${post?.id}', style: TextStyle(color: textColor)),
      trailing:
          Text('user ${post?.userId}', style: TextStyle(color: textColor)),
    );

/// Since [AsyncResource] uses [Future]s instead of [Stream]s, it only needs to
/// be created once. See [StreamedPostsWithHelpers] for this same example with
/// streams.
///
/// This class uses only the [FutureHandler] helper.
class PostsWithHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => RefreshIndicator(
        onRefresh: () => resources.posts.get(forceReload: true),
        child: FutureHandler<Iterable<Post>>(
          future: resources.posts.get(),
          initialData: resources.posts.data,
          handler: (context, posts) =>
              ListView(children: posts.map(_buildPost).toList()),
        ),
      );
}

/// Plain usage of [AsyncResource].
class PostsWithoutHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => RefreshIndicator(
        onRefresh: () => resources.posts.get(forceReload: true),
        child: FutureBuilder<Iterable<Post>>(
            future: resources.posts.get(),
            initialData: resources.posts.data,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return ListView(
                    children: snapshot.data.map(_buildPost).toList());
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              } else {
                return Center(child: CircularProgressIndicator());
              }
            }),
      );
}

/// [StreamedResource] use streams that must be managed as part of widget state.
/// More specifically, they must be created and destroyed when the widget
/// changes.
///
/// In this example, state management is handled by [ResourceProviderRoot]. See
/// [StreamedPostsWithoutHelpers] for an example that only uses plain Flutter
/// widgets to manage the resource.
class StreamedPostsWithHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => ResourceProviderRoot<Iterable<Post>>(
        onInit: () => StreamedResource(resources.posts),
        // Builder introduces a new context so we can use the above provider.
        child: Builder(
          builder: (context) => RefreshIndicator(
                onRefresh: () async =>
                    ResourceProvider.of(context).sink.add(true),
                child: ResourceWidget<Iterable<Post>>(
                  (context, posts) => ListView(
                        children: posts.map(_buildPost).toList(),
                      ),
                ),
              ),
        ),
      );
}

/// Plain use of [StreamedResource]. This example manually implements
/// [ResourceProvider]. In practice you may have a class with multiple
/// resources, and in that case you would create your own provider class.
///
/// The name "provider" is likely going to fade as instead of extending
/// [InheritedWidget], new implementations will extend [InheritedModel], and
/// naming conventions will be to use "...Model" instead of "...Provider". Once
/// [InheritedModel] makes it to the beta channel (or 1.0 release), it will be
/// used in this package instead of InheritedWidget.
class StreamedPostsWithoutHelpers extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _StreamedPostsWithoutHelpersState();
}

class _StreamedPostsWithoutHelpersState
    extends State<StreamedPostsWithoutHelpers> {
  StreamedResource<Iterable<Post>> resource;

  @override
  Widget build(BuildContext context) => ResourceProvider<Iterable<Post>>(
        resource: resource,
        // Builder introduces a new context so we can use the above provider.
        child: Builder(
          builder: (context) => RefreshIndicator(
                onRefresh: () async =>
                    ResourceProvider.of(context).sink.add(true),
                child: StreamBuilder<Iterable<Post>>(
                    stream: resource.stream,
                    initialData: resources.posts.data,
                    builder: (context, snapshot) {
                      if (snapshot.hasData) {
                        return ListView(
                            children: snapshot.data.map(_buildPost).toList());
                      } else if (snapshot.hasError) {
                        return Text('${snapshot.error}');
                      } else {
                        return Center(child: CircularProgressIndicator());
                      }
                    }),
              ),
        ),
      );

  @override
  void initState() {
    super.initState();
    resource = StreamedResource(resources.posts);
    resource.sink.add(false);
  }

  @override
  void dispose() {
    resource.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(StreamedPostsWithoutHelpers oldWidget) {
    super.didUpdateWidget(oldWidget);
    resource.dispose();
    resource = StreamedResource(resources.posts);
  }
}

[0.1.1] - November 25, 2018

  • Minor changes to the ResourceProvider helper class.
  • Upgrade dependencies

[0.1.0] - October 3, 2018

  • First release. Interchangeable with the now deprecated shared_prefs_resource.

example/main.dart

// See the working example at
// https://github.com/jifalops/async_resource/blob/master/example/packages/mobile/lib/main.dart
import 'package:flutter/material.dart';
import 'package:async_resource_flutter/async_resource_flutter.dart';
// import 'package:async_resource_example/config.dart';
import 'src/resources.dart';


Color textColor = Colors.black;

void main() => runApp(MaterialApp(
    title: appName,
    home: FutureHandler(
      // This looks up the application documents directory so network requests
      // know where to cache results.
      future: MobileResources.init(),
      handler: (context, resources) => HomePage(),
    )));

class HomePage extends StatefulWidget {
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  static const lightBg = Color(0xFFfefefe);
  static const darkBg = Color(0xFF333333);

  int selectedPage = 0;
  Color bgColor = lightBg;

  @override
  void initState() {
    super.initState();
    resources.darkBackground.get().then((isDark) => setState(() {
          bgColor = isDark ? darkBg : lightBg;
          textColor = isDark ? Colors.white : Colors.black;
        }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: bgColor,
      appBar: AppBar(
        title: Text(appName),
        actions: <Widget>[
          Switch(value: bgColor == darkBg, onChanged: (_) => _toggleColor())
        ],
      ),
      body: _buildBody(context),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        currentIndex: selectedPage,
        onTap: (index) {
          if (index != selectedPage) setState(() => selectedPage = index);
        },
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.ac_unit), title: Text('Streamed\nHelpers')),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings_remote),
              title: Text('Future\nHelpers')),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings_input_antenna),
              title: Text('Streamed')),
          BottomNavigationBarItem(
              icon: Icon(Icons.shuffle), title: Text('Future')),
        ],
      ),
    );
  }

  Widget _buildBody(BuildContext context) {
    switch (selectedPage) {
      case 1:
        return PostsWithHelpers();
      case 2:
        return StreamedPostsWithoutHelpers();
      case 3:
        return PostsWithoutHelpers();
      case 0:
      default:
        return StreamedPostsWithHelpers();
    }
  }

  void _toggleColor() => setState(() {
        final turningDark = bgColor == lightBg;
        bgColor = turningDark ? darkBg : lightBg;
        textColor = turningDark ? Colors.white : Colors.black;
        resources.darkBackground.write(turningDark);
      });
}

//
// Different ways of building ListTiles from a `Post`.
//

Widget _buildPost(Post post) => ListTile(
      title: Text('${post?.title}', style: TextStyle(color: textColor)),
      subtitle: Text('${post?.body}', style: TextStyle(color: textColor)),
      leading: Text('${post?.id}', style: TextStyle(color: textColor)),
      trailing:
          Text('user ${post?.userId}', style: TextStyle(color: textColor)),
    );

/// Since [AsyncResource] uses [Future]s instead of [Stream]s, it only needs to
/// be created once. See [StreamedPostsWithHelpers] for this same example with
/// streams.
///
/// This class uses only the [FutureHandler] helper.
class PostsWithHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => RefreshIndicator(
        onRefresh: () => resources.posts.get(forceReload: true),
        child: FutureHandler<Iterable<Post>>(
          future: resources.posts.get(),
          initialData: resources.posts.data,
          handler: (context, posts) =>
              ListView(children: posts.map(_buildPost).toList()),
        ),
      );
}

/// Plain usage of [AsyncResource].
class PostsWithoutHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => RefreshIndicator(
        onRefresh: () => resources.posts.get(forceReload: true),
        child: FutureBuilder<Iterable<Post>>(
            future: resources.posts.get(),
            initialData: resources.posts.data,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return ListView(
                    children: snapshot.data.map(_buildPost).toList());
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              } else {
                return Center(child: CircularProgressIndicator());
              }
            }),
      );
}

/// [StreamedResource] use streams that must be managed as part of widget state.
/// More specifically, they must be created and destroyed when the widget
/// changes.
///
/// In this example, state management is handled by [ResourceProviderRoot]. See
/// [StreamedPostsWithoutHelpers] for an example that only uses plain Flutter
/// widgets to manage the resource.
class StreamedPostsWithHelpers extends StatelessWidget {
  @override
  Widget build(BuildContext context) => ResourceProviderRoot<Iterable<Post>>(
        onInit: () => StreamedResource(resources.posts),
        // Builder introduces a new context so we can use the above provider.
        child: Builder(
          builder: (context) => RefreshIndicator(
                onRefresh: () async =>
                    ResourceProvider.of(context).sink.add(true),
                child: ResourceWidget<Iterable<Post>>(
                  (context, posts) => ListView(
                        children: posts.map(_buildPost).toList(),
                      ),
                ),
              ),
        ),
      );
}

/// Plain use of [StreamedResource]. This example manually implements
/// [ResourceProvider]. In practice you may have a class with multiple
/// resources, and in that case you would create your own provider class.
///
/// The name "provider" is likely going to fade as instead of extending
/// [InheritedWidget], new implementations will extend [InheritedModel], and
/// naming conventions will be to use "...Model" instead of "...Provider". Once
/// [InheritedModel] makes it to the beta channel (or 1.0 release), it will be
/// used in this package instead of InheritedWidget.
class StreamedPostsWithoutHelpers extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _StreamedPostsWithoutHelpersState();
}

class _StreamedPostsWithoutHelpersState
    extends State<StreamedPostsWithoutHelpers> {
  StreamedResource<Iterable<Post>> resource;

  @override
  Widget build(BuildContext context) => ResourceProvider<Iterable<Post>>(
        resource: resource,
        // Builder introduces a new context so we can use the above provider.
        child: Builder(
          builder: (context) => RefreshIndicator(
                onRefresh: () async =>
                    ResourceProvider.of(context).sink.add(true),
                child: StreamBuilder<Iterable<Post>>(
                    stream: resource.stream,
                    initialData: resources.posts.data,
                    builder: (context, snapshot) {
                      if (snapshot.hasData) {
                        return ListView(
                            children: snapshot.data.map(_buildPost).toList());
                      } else if (snapshot.hasError) {
                        return Text('${snapshot.error}');
                      } else {
                        return Center(child: CircularProgressIndicator());
                      }
                    }),
              ),
        ),
      );

  @override
  void initState() {
    super.initState();
    resource = StreamedResource(resources.posts);
    resource.sink.add(false);
  }

  @override
  void dispose() {
    resource.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(StreamedPostsWithoutHelpers oldWidget) {
    super.didUpdateWidget(oldWidget);
    resource.dispose();
    resource = StreamedResource(resources.posts);
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  async_resource_flutter: ^0.1.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:async_resource_flutter/async_resource_flutter.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
56
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
76
Learn more about scoring.

We analyzed this package on Sep 17, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.0
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.2

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (shared_preferences).

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
async_resource ^0.1.0 0.1.4
flutter 0.0.0
shared_preferences ^0.4.0 0.4.3 0.5.3+4
Transitive dependencies
async 2.3.0
charcode 1.1.2
collection 1.14.11 1.14.12
http 0.12.0+2
http_parser 3.1.3
js 0.6.1+1
meta 1.1.7
path 1.6.4
pedantic 1.8.0+1
rxdart 0.18.1 0.22.2
service_worker 0.2.3
sky_engine 0.0.99
source_span 1.5.5
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test