Skip to content

Commit 18511db

Browse files
committed
docs: Add 'rendering_stac_widgets' page to Concepts section in documentation
1 parent dc8a145 commit 18511db

File tree

2 files changed

+380
-0
lines changed

2 files changed

+380
-0
lines changed
Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
---
2+
title: "Rendering Stac Widgets"
3+
description: "Learn different ways to render Stac widgets: from Stac Cloud, local JSON, assets, and network requests"
4+
---
5+
6+
Stac provides multiple ways to render widgets from JSON, each suitable for different scenarios. This guide covers all available rendering methods and when to use them.
7+
8+
## Prerequisites
9+
10+
Before rendering any Stac widgets, you must initialize Stac in your application:
11+
12+
```dart
13+
import 'package:stac/stac.dart';
14+
import 'package:your_app/default_stac_options.dart';
15+
16+
void main() async {
17+
await Stac.initialize(options: defaultStacOptions);
18+
runApp(const MyApp());
19+
}
20+
```
21+
22+
## Rendering Methods
23+
24+
### 1. From Stac Cloud (`Stac` Widget)
25+
26+
The most common approach for server-driven UI is fetching screens from Stac Cloud using the `Stac` widget.
27+
28+
#### Usage
29+
30+
```dart
31+
Stac(routeName: 'home_screen')
32+
```
33+
34+
This widget automatically fetches the screen JSON from Stac Cloud based on the `routeName` and renders it.
35+
36+
#### Dart Source Code
37+
38+
The `home_screen` is defined in your `/stac` folder as a Dart file. Here's an example:
39+
40+
**`stac/home_screen.dart`:**
41+
42+
```dart
43+
import 'package:stac_core/stac_core.dart';
44+
45+
@StacScreen(screenName: 'home_screen')
46+
StacWidget homeScreen() {
47+
return StacScaffold(
48+
appBar: StacAppBar(title: StacText(data: 'Home')),
49+
body: StacColumn(
50+
children: [
51+
StacText(data: 'Welcome to Stac!'),
52+
StacElevatedButton(
53+
onPressed: {
54+
'actionType': 'navigate',
55+
'routeName': 'details'
56+
},
57+
child: StacText(data: 'Go to Details'),
58+
),
59+
],
60+
),
61+
);
62+
}
63+
```
64+
65+
After running `stac deploy`, this Dart code is converted to JSON and uploaded to Stac Cloud, making it available via `Stac(routeName: 'home_screen')`.
66+
67+
#### Properties
68+
69+
| Property | Type | Description |
70+
|----------------|----------|------------------------------------------------|
71+
| `routeName` | `String` | The screen name registered in Stac Cloud |
72+
| `loadingWidget`| `Widget?`| Custom widget shown while fetching (optional) |
73+
| `errorWidget` | `Widget?`| Custom widget shown on error (optional) |
74+
75+
#### Example
76+
77+
```dart
78+
import 'package:flutter/material.dart';
79+
import 'package:stac/stac.dart';
80+
81+
class MyApp extends StatelessWidget {
82+
const MyApp({super.key});
83+
84+
@override
85+
Widget build(BuildContext context) {
86+
return MaterialApp(
87+
title: 'Stac Demo',
88+
home: Stac(
89+
routeName: 'hello_world',
90+
loadingWidget: const Center(child: CircularProgressIndicator()),
91+
errorWidget: const Center(child: Text('Failed to load screen')),
92+
),
93+
);
94+
}
95+
}
96+
```
97+
98+
#### When to Use
99+
100+
- ✅ Production apps using Stac Cloud
101+
- ✅ Dynamic content that changes server-side
102+
- ✅ A/B testing and experimentation
103+
- ✅ Apps that need instant updates without app store approval
104+
105+
### 2. From JSON (`Stac.fromJson`)
106+
107+
Render a widget directly from a JSON map. Useful for testing, prototyping, or when you have JSON in memory.
108+
109+
#### Usage
110+
111+
```dart
112+
Stac.fromJson(jsonMap, context)
113+
```
114+
115+
#### Properties
116+
117+
| Parameter | Type | Description |
118+
|----------------|--------------------------|----------------------------------------|
119+
| `json` | `Map<String, dynamic>?` | The JSON object representing the widget|
120+
| `context` | `BuildContext` | The build context |
121+
122+
#### Example
123+
124+
```dart
125+
import 'package:flutter/material.dart';
126+
import 'package:stac/stac.dart';
127+
128+
class HomeScreen extends StatelessWidget {
129+
const HomeScreen({super.key});
130+
131+
@override
132+
Widget build(BuildContext context) {
133+
final json = {
134+
'type': 'scaffold',
135+
'body': {
136+
'type': 'center',
137+
'child': {
138+
'type': 'text',
139+
'data': 'Hello from JSON!'
140+
}
141+
}
142+
};
143+
144+
return Stac.fromJson(json, context) ?? const SizedBox();
145+
}
146+
}
147+
```
148+
149+
#### When to Use
150+
151+
- ✅ Testing and development
152+
- ✅ Prototyping with hardcoded JSON
153+
- ✅ Rendering widgets from local variables
154+
- ✅ Converting existing JSON data to widgets
155+
156+
### 3. From Assets (`Stac.fromAssets`)
157+
158+
Load and render widgets from JSON files bundled with your app. Perfect for static content or offline-first scenarios.
159+
160+
#### Usage
161+
162+
```dart
163+
Stac.fromAssets(
164+
'assets/screens/home.json',
165+
loadingWidget: (context) => const CircularProgressIndicator(),
166+
errorWidget: (context, error) => Text('Error: $error'),
167+
)
168+
```
169+
170+
#### Properties
171+
172+
| Parameter | Type | Description |
173+
|-----------------|---------------------------|------------------------------------------------|
174+
| `assetPath` | `String` | Path to the JSON file in your assets folder |
175+
| `loadingWidget` | `LoadingWidgetBuilder?` | Widget shown while loading (optional) |
176+
| `errorWidget` | `ErrorWidgetBuilder?` | Widget shown on error (optional) |
177+
178+
#### Setup
179+
180+
First, add your JSON file to `pubspec.yaml`:
181+
182+
```yaml
183+
flutter:
184+
assets:
185+
- assets/screens/home.json
186+
```
187+
188+
#### Example
189+
190+
```dart
191+
import 'package:flutter/material.dart';
192+
import 'package:stac/stac.dart';
193+
194+
class OfflineScreen extends StatelessWidget {
195+
const OfflineScreen({super.key});
196+
197+
@override
198+
Widget build(BuildContext context) {
199+
return Stac.fromAssets(
200+
'assets/screens/home.json',
201+
loadingWidget: (context) => const Scaffold(
202+
body: Center(child: CircularProgressIndicator()),
203+
),
204+
errorWidget: (context, error) => Scaffold(
205+
body: Center(
206+
child: Column(
207+
mainAxisAlignment: MainAxisAlignment.center,
208+
children: [
209+
const Icon(Icons.error_outline, size: 48),
210+
const SizedBox(height: 16),
211+
Text('Failed to load: $error'),
212+
],
213+
),
214+
),
215+
),
216+
);
217+
}
218+
}
219+
```
220+
221+
#### When to Use
222+
223+
- ✅ Static content that doesn't change
224+
- ✅ Offline-first applications
225+
- ✅ Fallback screens when network fails
226+
- ✅ Demo apps and prototypes
227+
228+
### 4. From Network (`Stac.fromNetwork`)
229+
230+
Fetch and render widgets from any HTTP endpoint. Provides more control than Stac Cloud and works with your own API.
231+
232+
#### Usage
233+
234+
```dart
235+
Stac.fromNetwork(
236+
context: context,
237+
request: StacNetworkRequest(
238+
url: 'https://api.example.com/ui/screen',
239+
method: Method.get,
240+
),
241+
loadingWidget: (context) => const CircularProgressIndicator(),
242+
errorWidget: (context, error) => Text('Error: $error'),
243+
)
244+
```
245+
246+
#### Properties
247+
248+
| Parameter | Type | Description |
249+
|-----------------|---------------------------|------------------------------------------------|
250+
| `context` | `BuildContext` | The build context |
251+
| `request` | `StacNetworkRequest` | Network request configuration |
252+
| `loadingWidget` | `LoadingWidgetBuilder?` | Widget shown while loading (optional) |
253+
| `errorWidget` | `ErrorWidgetBuilder?` | Widget shown on error (optional) |
254+
255+
#### StacNetworkRequest Properties
256+
257+
| Property | Type | Description |
258+
|------------------|----------------------------|------------------------------------------------|
259+
| `url` | `String` | The URL to fetch JSON from |
260+
| `method` | `Method` | HTTP method (get, post, put, delete) |
261+
| `headers` | `Map<String, dynamic>?` | HTTP headers (e.g., Authorization) |
262+
| `queryParameters`| `Map<String, dynamic>?` | URL query parameters |
263+
| `body` | `dynamic` | Request body for POST/PUT |
264+
| `contentType` | `String?` | Content-Type header (e.g., application/json) |
265+
266+
#### Example
267+
268+
```dart
269+
import 'package:flutter/material.dart';
270+
import 'package:stac/stac.dart';
271+
import 'package:stac_core/actions/network_request/stac_network_request.dart';
272+
273+
class ApiDrivenScreen extends StatelessWidget {
274+
const ApiDrivenScreen({super.key});
275+
276+
@override
277+
Widget build(BuildContext context) {
278+
return Stac.fromNetwork(
279+
context: context,
280+
request: StacNetworkRequest(
281+
url: 'https://api.example.com/ui/home',
282+
method: Method.get,
283+
headers: {
284+
'Authorization': 'Bearer your-token-here',
285+
'Accept': 'application/json',
286+
},
287+
),
288+
loadingWidget: (context) => const Scaffold(
289+
body: Center(child: CircularProgressIndicator()),
290+
),
291+
errorWidget: (context, error) => Scaffold(
292+
body: Center(
293+
child: Column(
294+
mainAxisAlignment: MainAxisAlignment.center,
295+
children: [
296+
const Icon(Icons.cloud_off, size: 48),
297+
const SizedBox(height: 16),
298+
Text('Network error: $error'),
299+
const SizedBox(height: 16),
300+
ElevatedButton(
301+
onPressed: () {
302+
// Retry logic
303+
},
304+
child: const Text('Retry'),
305+
),
306+
],
307+
),
308+
),
309+
),
310+
);
311+
}
312+
}
313+
```
314+
315+
#### POST Request Example
316+
317+
```dart
318+
Stac.fromNetwork(
319+
context: context,
320+
request: StacNetworkRequest(
321+
url: 'https://api.example.com/ui/dynamic',
322+
method: Method.post,
323+
headers: {
324+
'Content-Type': 'application/json',
325+
'Authorization': 'Bearer token',
326+
},
327+
body: {
328+
'userId': '123',
329+
'featureFlags': ['new-ui', 'experiments'],
330+
},
331+
),
332+
)
333+
```
334+
335+
#### When to Use
336+
337+
- ✅ Custom API endpoints
338+
- ✅ Server-side rendering
339+
- ✅ Dynamic content based on user data
340+
- ✅ Multi-tenant applications
341+
342+
## Comparison
343+
344+
| Method | Source | Best For | Network Required |
345+
|---------------------|---------------------|---------------------------------------|------------------|
346+
| `Stac(routeName:)` | Stac Cloud | Production SDUI apps | ✅ Yes |
347+
| `Stac.fromJson()` | In-memory JSON | Testing, prototyping | ❌ No |
348+
| `Stac.fromAssets()` | Bundled JSON file | Offline, static content | ❌ No |
349+
| `Stac.fromNetwork()`| Custom API endpoint | Custom backends, advanced use cases | ✅ Yes |
350+
351+
## Best Practices
352+
353+
1. **Always provide loading states**: Users should know content is loading.
354+
2. **Handle errors gracefully**: Show meaningful error messages and retry options.
355+
3. **Use appropriate method**: Choose the rendering method that fits your use case.
356+
5. **Validate JSON**: Ensure your JSON follows Stac schema before rendering.
357+
358+
### Hybrid Approach
359+
360+
```dart
361+
class HybridScreen extends StatelessWidget {
362+
@override
363+
Widget build(BuildContext context) {
364+
// Try cloud first, fallback to assets
365+
return FutureBuilder<bool>(
366+
future: checkNetworkConnection(),
367+
builder: (context, snapshot) {
368+
if (snapshot.data == true) {
369+
return Stac(routeName: 'home_screen');
370+
} else {
371+
return Stac.fromAssets('assets/screens/home.json');
372+
}
373+
},
374+
);
375+
}
376+
}
377+
```
378+

docs/docs.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
},
3535
{
3636
"group": "Concepts",
37+
"icon": "book",
3738
"pages": [
39+
"concepts/rendering_stac_widgets",
3840
"concepts/action_parsers",
3941
"concepts/parsers",
4042
"concepts/theming"

0 commit comments

Comments
 (0)