| name | genui_workshop_step_6 |
| description | Execute Step 6 of the GenUI Workshop, creating the weather input widget and updating the system prompt. |
GenUI Workshop - Step 6
Goal: Execute Step 6 of the GenUI workshop.
Instructions:
Use your code editing tools to create the weather input catalog widget and update the app's configuration.
- Create
lib/catalog/weather_input.dart:
import 'package:flutter/material.dart';
import 'package:genui/genui.dart';
import 'package:json_schema_builder/json_schema_builder.dart';
final simpleWeatherSchema = S.object(
properties: {
'location': S.string(description: 'The location to check the weather.'),
'date': S.string(description: 'The date to check the weather.'),
},
);
final weatherInput = CatalogItem(
name: 'WeatherInput',
dataSchema: simpleWeatherSchema,
widgetBuilder: (itemContext) {
final json = itemContext.data as Map<String, Object?>;
final data = SimpleWeatherData.fromJson(json);
return WeatherInput(
data: data,
onFetchRequest: (loc, date) async {
final JsonMap resolvedContext = await resolveContext(
itemContext.dataContext,
{'location': loc, 'date': date.toString()},
);
itemContext.dispatchEvent(
UserActionEvent(
name: 'submit_weather_request',
sourceComponentId: 'submitButton',
timestamp: DateTime.now(),
context: resolvedContext
),
);
},
);
},
);
class SimpleWeatherData {
String location;
DateTime date;
SimpleWeatherData({required this.location, required this.date});
factory SimpleWeatherData.defaultValues() {
return SimpleWeatherData(location: 'Baltimore', date: DateTime.now());
}
factory SimpleWeatherData.fromJson(Map<String, Object?> json) {
if (json.isNotEmpty) {
return SimpleWeatherData(
location: json['location'] as String,
date: DateTime.parse(json['date'] as String),
);
} else {
return SimpleWeatherData(location: 'Baltimore', date: DateTime.now());
}
}
}
class WeatherInput extends StatefulWidget {
final SimpleWeatherData data;
final void Function(String, DateTime) onFetchRequest;
const WeatherInput({
super.key,
required this.data,
required this.onFetchRequest,
});
@override
State<WeatherInput> createState() => _WeatherInputState();
}
class _WeatherInputState extends State<WeatherInput> {
late TextEditingController _controller;
DateTime? selectedDate = DateTime.now();
Future<void> _selectDate() async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now().copyWith(month: 1, day: 1),
lastDate: DateTime.now().copyWith(month: 12, day: 31),
);
setState(() {
selectedDate = pickedDate;
});
}
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: 320,
padding: const EdgeInsets.all(16),
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Location", style: TextStyle(fontWeight: FontWeight.bold) ),
const SizedBox(height: 8),
TextField(decoration: InputDecoration(border: OutlineInputBorder()), controller: _controller,),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 50,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
),
onPressed: () {
widget.onFetchRequest(_controller.text, selectedDate!);
},
child: const Text("Get Forecast", style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold)),
),
),
],
),
),
),
);
}
}