flutter_remoter

Last updated:

0 purchases

flutter_remoter Image
flutter_remoter Images
Add to Cart

Description:

flutter remoter

Remoter aims to simplify handling asynchronous operations and revalidating them, inspired by React Query
Features #

Global and individual options
Cache is collected after cache time if there is no listener
Query is refetched if query is stale
Fetch only once when multiple widget mounts at the same time
Pagination
Invalidate query
Set query data manually
Retry query when new widget mounts
Auto retry with exponential backoff
Mutation widget

Getting started #
Install dependencies #
dependencies:
flutter_remoter: ^0.2.0
copied to clipboard
Wrap your app with RemoterProvider #
RemoterProvider expects a RemoterClient which you can export from package and use everywhere without context.
class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return RemoterProvider(
client: RemoterClient(
// This line defines default options for all queries
// You can override options in each query
options: RemoterOptions(
// staleTime defines how many ms after query fetched can be refetched
// Use infinite staleTime if you don't need queries to be refetched when new query mounts
// 1 << 31 is max int32
// default is 0ms
staleTime: 0,
// cacheTime defines how many ms after all listeners are gone query data should be cleared,
// default is 5 minutes
cacheTime: 5 * 60 * 1000,
// Maximum delay between retries in ms
maxDelay: 5 * 60 * 1000,
// Maximum amount of retries
maxRetries: 3,
// Flag that decides if query that has error status should be refetched on mount
retryOnMount: true,
),
),
child: const MaterialApp(
home: MyHomePage(),
),
);
}
}
copied to clipboard
Usage #
There are three types of widgets: RemoterQuery, PaginatedRemoterQuery and RemoterMutation.
Remoter Query #
Used for 'single page' data

See full example

RemoterQuery<T>(
remoterKey: "key",
listener: (oldState, newState) async {
// Optional state listener
},
execute: () async {
// Fetch data here
},
// Override default options defined in RemoterClient
// You don't have to copy the fields you don't want to override
// e.g Default is RemoterOptions(cacheTime: 2000, staleTime: 1000).
// You want to override staleTime for specific query, use RemoterOptions(staleTime: 1000).
// In this case cacheTime won't be overriden and will still be 2000
options: RemoterOptions(),
// Query won't start if this is true
disabled: false,
builder: (context, snapshot, utils) {
if (snapshot.status == RemoterStatus.idle) {
// You can skip this check if you don't use disabled parameter
}
if (snapshot.status == RemoterStatus.fetching) {
// Handle fetching state here
}
if (snapshot.status == RemoterStatus.error) {
// Handle error here
}
// It is okay to use snapshot.data! here
return ...
},
)
copied to clipboard
Paginated Remoter Query #
Used for data that has multiple pages or "infinite scroll" like experience.

See full example

PaginatedRemoterQuery<FactsPage>(
// remoterKey should be unique
remoterKey: "facts",
// Data returned from these functions will be passed
// as param to execute function
getNextPageParam: (pages) {
return pages[pages.length - 1].nextPage;
},
getPreviousPageParam: (pages) {
return pages[0].previousPage;
},
// Override default options defined in RemoterClient
// You don't have to copy the fields you don't want to override
// e.g Default is RemoterOptions(cacheTime: 2000, staleTime: 1000).
// You want to override staleTime for specific query, use RemoterOptions(staleTime: 1000).
// In this case cacheTime won't be overriden and will still be 2000
options: RemoterOptions(),
execute: (param) async {
// Fetch data here
},
// Query won't start if this is true
disabled: false,
builder: (context, snapshot, utils) {
if (snapshot.status == RemoterStatus.idle) {
// You can skip this check if you don't use disabled parameter
}
if (snapshot.status == RemoterStatus.fetching) {
// Handle fetching state here
}
if (snapshot.status == RemoterStatus.error) {
// Handle error here
}
// It is okay to use snapshot.data! here
return SingleChildScrollView(
child: Column(
children: [
if (snapshot.hasPreviousPage)
ElevatedButton(
onPressed: () {
utils.fetchPreviousPage();
},
child: snapshot.isFetchingPreviousPage == true
? const CircularProgressIndicator(
color: Colors.white,
)
: const Text("Load previous"),
),
...snapshot.data!
.expand((el) => el.facts)
.map(
(d) => Text(d.fact),
)
.toList(),
if (snapshot.hasNextPage)
ElevatedButton(
onPressed: () {
utils.fetchNextPage();
},
child: snapshot.isFetchingNextPage == true
? const CircularProgressIndicator(
color: Colors.white,
)
: const Text("Load more"),
),
],
),
);
})

copied to clipboard
Remoter Mutation #
Used to simplify handling asynchronous calls
T represents type of the value execute function returns
S represents type of the value passed to mutate function which will be passed to execute function as parameter

See example

RemoterMutation<T, S>(
execute: (param) async {
await Future.delayed(const Duration(seconds: 1));
return ...
},
builder: (context, snapshot, utils) {
if (snapshot.status == RemoterStatus.idle) {
// Mutation hasn't started yet
}
if (snapshot.status == RemoterStatus.fetching) {
// Handle fetching state here
}
if (snapshot.status == RemoterStatus.error) {
// Handle error here
}
// It is okay to use snapshot.data! here
return Text(
snapshot.data!,
);
},
floatingActionButton: FloatingActionButton(
onPressed: snapshot.status == RemoterStatus.fetching
? null
: () {
// Starts mutation
// In this case null will be passed to execute as param
utils.mutate(null);
},
child: const Icon(Icons.add),
),
);
});
copied to clipboard
Using RemoterClient #
There are 2 ways to retrieve RemoterClient
With BuildContext #
RemoterProvider.of(context).client
copied to clipboard
Without BuildContext #
To use RemoterClient without context, you can create RemoterClient in separate file.
Then that instance should be use with RemoterProvider which wraps the App.
Finally, you can import and use the instance anywhere in your app.
import 'path to RemoterClient instance';

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return RemoterProvider(
// 'client' is the instance from import
client: client,
child: const MaterialApp(
home: MyHomePage(),
),
);
}
}
copied to clipboard

License:

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Files In This Product:

Customer Reviews

There are no reviews.