Skip to content

Introduction to Fasq

Fasq (Flutter Async State Query) is a powerful, developer-friendly state management solution for asynchronous data in Flutter. It handles fetching, caching, synchronizing, and updating server state in your application with zero boilerplate.

Managing server state in Flutter can be challenging. You often need to:

  • Show loading spinners while fetching data
  • Handle errors gracefully
  • Cache data to save bandwidth and improve performance
  • Update UI instantly when data changes
  • Refresh data in the background

Fasq handles all of this for you with a simple, widget-based API that feels native to Flutter.

Add the core package to your project:

Terminal window
flutter pub add fasq

[!NOTE] If you are using a state management library like Riverpod, Bloc, or Hooks, check out our optimized adapters for a better developer experience.

Fasq is designed to work out of the box. You can start using it immediately without any global setup. Just import the package and use QueryBuilder:

import 'package:flutter/material.dart';
import 'package:fasq/fasq.dart';
class TodoList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return QueryBuilder<List<Todo>>(
// specialized extension to convert string to QueryKey
queryKey: 'todos'.toQueryKey(),
// check out generic type support
queryFn: () => api.fetchTodos(),
builder: (context, state) {
if (state.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (state.hasError) {
return Center(child: Text('Error: ${state.error}'));
}
if (state.hasData) {
return ListView.builder(
itemCount: state.data!.length,
itemBuilder: (context, index) {
final todo = state.data![index];
return ListTile(title: Text(todo.title));
},
);
}
return const SizedBox();
},
);
}
}

For production apps, we recommend configuring a global QueryClient at the root of your application. This allows you to define global cache policies, default timeouts, and share the client across your app.

import 'package:flutter/material.dart';
import 'package:fasq/fasq.dart';
void main() {
// 1. Create a client with your preferred configuration
final client = QueryClient(
config: const CacheConfig(
defaultCacheTime: Duration(minutes: 10), // Keep unused data for 10 mins
defaultStaleTime: Duration(minutes: 5), // Consider data fresh for 5 mins
),
);
runApp(
// 2. Wrap your app with QueryClientProvider
QueryClientProvider(
client: client,
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fasq Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Fasq Demo')),
body: const TodoList(), // Your app content
),
);
}
}

Fasq comes packed with features to make your life easier:

  • Automatic Caching: Data is cached automatically. Navigation back to a screen shows data instantly.
  • Background Refetching: Stale data is shown immediately while fresh data is fetched in the background.
  • Request Deduplication: Multiple widgets asking for the same data? Only one network request is made.
  • Window Focus Refetching: Automatically refetch data when the app comes back to the foreground.
  • Optimisitc Updates: Update your UI immediately when performing mutations, then sync with the server.
  • Infinite Queries: Built-in support for infinite scrolling lists.
  • Built-in Logging: Development-friendly logging with FasqLogger to debug query and mutation lifecycle events.

Now that you have the basics, dive deeper into the core concepts: