Skip to content

Query

The @SolidQuery() annotation allows you to create reactive state based on asynchronous data sources, such as fetching data from a network.

source/query_example.dart
class QueryExample extends StatelessWidget {
const QueryExample({super.key});
@SolidQuery()
Future<String> fetchData() async {
await Future.delayed(const Duration(seconds: 1));
return 'Fetched Data';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Query')),
body: Center(
child: fetchData().when(
ready: (data) => Text(data),
loading: () => CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error: $error'),
),
),
);
}
}

This example defines a fetchData method annotated with @SolidQuery(), which simulates fetching data asynchronously. The when method is used to handle the build different widgets based on the states of the query: ready, loading, and error.


You can also use the @SolidQuery() annotation for streams.

source/query_example.dart
@SolidQuery()
Stream<int> fetchData() {
return Stream.periodic(const Duration(seconds: 1), (i) => i);
}

And magically, you don’t have to change anything else.

A query can also depend on reactive state variables.

source/query_with_source_example.dart
class QueryWithSourceExample extends StatelessWidget {
QueryWithSourceExample({super.key});
@SolidState()
String? userId;
@SolidQuery(debounce: Duration(seconds: 1))
Future<String?> fetchData() async {
if (userId == null) return null;
await Future.delayed(const Duration(seconds: 1));
return 'Fetched Data for $userId';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('QueryWithSource')),
body: Center(
child: fetchData().when(
ready: (data) {
if (data == null) {
return const Text('No user ID provided');
}
return Text(data);
},
loading: () => CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error: $error'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
userId = 'user_${DateTime.now().millisecondsSinceEpoch}',
child: const Icon(Icons.refresh),
),
);
}
}

By default, when a query is re-executed due to a dependency change, it doesn’t enter the loading state again. Instead, it stays in the current state while the new data is being fetched. When the new data arrives, the state updates accordingly. This behavior is designed to provide a smoother user experience by avoiding unnecessary loading indicators during data refreshes.

You can detect if a query is currently refreshing by using the isRefreshing property.

fetchData().isRefreshing

This property returns true if the query is in the process of refreshing its data, allowing you to adjust your UI accordingly (e.g., showing a subtle loading indicator or a refresh icon).

You can disable this behavior by setting the useRefreshing parameter to false in the @SolidQuery() annotation.

@SolidQuery(useRefreshing: false)

In this case, the query will enter the loading state again when it is re-executed due to a dependency change.

You can manually refresh a query by calling the refresh method on it.

fetchData.refresh();