gemini_widgets 0.0.3
gemini_widgets: ^0.0.3 copied to clipboard
A powerful Flutter package for declarative AI widgets. Simply generate text, images, and vision insights using Google's Gemini API with minimal boilerplate and built-in state management.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:gemini_widgets/gemini_widgets.dart';
import 'package:image_picker/image_picker.dart';
void main() {
// IMPORTANT: Replace with your actual Gemini API key.
GeminiWidgets.initialize(apiKey: 'YOUR_GEMINI_API_KEY');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gemini Widgets Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
static const List<Widget> _pages = [
GenerativeTextDemo(),
GenerativeImageDemo(),
VisionTextDemo(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Gemini Widgets'),
elevation: 2,
),
body: _pages[_selectedIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() {
_selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.text_fields),
label: 'Text',
),
NavigationDestination(
icon: Icon(Icons.image),
label: 'Image',
),
NavigationDestination(
icon: Icon(Icons.remove_red_eye),
label: 'Vision',
),
],
),
);
}
}
class GenerativeTextDemo extends StatefulWidget {
const GenerativeTextDemo({super.key});
@override
State<GenerativeTextDemo> createState() => _GenerativeTextDemoState();
}
class _GenerativeTextDemoState extends State<GenerativeTextDemo> {
final _controller =
TextEditingController(text: "Write a short poem about coding.");
String _prompt = "Write a short poem about coding.";
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter Prompt',
suffixIcon: IconButton(
icon: const Icon(Icons.send),
onPressed: () {
setState(() {
_prompt = _controller.text;
});
},
),
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: GenerativeText(
prompt: _prompt,
loadingBuilder: (context) => const Center(
child: Column(
children: [
CircularProgressIndicator(),
SizedBox(height: 10),
Text("Thinking..."),
],
),
),
successBuilder: (context, text) => Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(text ?? "No result"),
),
),
errorBuilder: (context, error) => Text("Error: $error"),
),
),
),
],
),
);
}
}
class GenerativeImageDemo extends StatefulWidget {
const GenerativeImageDemo({super.key});
@override
State<GenerativeImageDemo> createState() => _GenerativeImageDemoState();
}
class _GenerativeImageDemoState extends State<GenerativeImageDemo> {
final _controller =
TextEditingController(text: "A magical forest with glowing mushrooms");
String _prompt = "A magical forest with glowing mushrooms";
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Image Description',
suffixIcon: IconButton(
icon: const Icon(Icons.brush),
onPressed: () {
setState(() {
_prompt = _controller.text;
});
},
),
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 20),
Expanded(
child: GenerativeImage(
prompt: _prompt,
loadingBuilder: (context) =>
const Center(child: CircularProgressIndicator()),
successBuilder: (context, image) => ClipRRect(
borderRadius: BorderRadius.circular(16),
child: image,
),
errorBuilder: (context, error) => Text("Error: $error"),
),
),
],
),
);
}
}
class VisionTextDemo extends StatefulWidget {
const VisionTextDemo({super.key});
@override
State<VisionTextDemo> createState() => _VisionTextDemoState();
}
class _VisionTextDemoState extends State<VisionTextDemo> {
Uint8List? _imageBytes;
final _picker = ImagePicker();
Future<void> _pickImage() async {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
final bytes = await image.readAsBytes();
setState(() {
_imageBytes = bytes;
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
ElevatedButton.icon(
onPressed: _pickImage,
icon: const Icon(Icons.add_a_photo),
label: const Text('Pick Image'),
),
const SizedBox(height: 20),
if (_imageBytes != null) ...[
SizedBox(
height: 200,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.memory(_imageBytes!, fit: BoxFit.cover),
),
),
const SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: VisionText(
prompt: "What do you see in this image?",
imageBytes: _imageBytes!,
loadingBuilder: (context) =>
const Center(child: CircularProgressIndicator()),
successBuilder: (context, text) => Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(text ?? "No description"),
),
),
errorBuilder: (context, error) => Text("Error: $error"),
),
),
),
] else
const Expanded(
child: Center(
child: Text('Please select an image to analyze'),
),
),
],
),
);
}
}