Last active
June 26, 2024 22:43
-
-
Save romanejaquez/d8dd3b36d39efce7f1c53a6db4969844 to your computer and use it in GitHub Desktop.
gemini_pictionary_fullcode.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dependencies | |
import 'dart:convert'; | |
import 'package:finger_painter/finger_painter.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:flutter_animate/flutter_animate.dart'; | |
import 'package:flutter_riverpod/flutter_riverpod.dart'; | |
import 'package:google_generative_ai/google_generative_ai.dart'; | |
import 'dart:ui' as ui; | |
// root widget | |
void main() { | |
runApp(const ProviderScope(child: GeminiPictionaryApp())); | |
} | |
class GeminiPictionaryApp extends StatelessWidget { | |
const GeminiPictionaryApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
home: GeminiPictionaryHome(), | |
); | |
} | |
} | |
class GeminiPictionaryHome extends StatelessWidget { | |
const GeminiPictionaryHome({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
backgroundColor: Colors.white, | |
elevation: 0, | |
centerTitle: true, | |
title: const Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Icon(Icons.collections, color: Colors.purple), | |
SizedBox(width: 16), | |
Text('Pictionary!', style: TextStyle(color: Colors.purple)) | |
], | |
), | |
), | |
body: const Center( | |
child: Column( | |
children: [ | |
PaintingSurface(), | |
PaintingValidationControls(), | |
PaintingValidator(), | |
], | |
) | |
) | |
); | |
} | |
} | |
class PaintingValidationControls extends ConsumerWidget { | |
const PaintingValidationControls({super.key}); | |
@override | |
Widget build(BuildContext context, WidgetRef ref) { | |
return Padding( | |
padding: const EdgeInsets.all(24), | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
ElevatedButton.icon( | |
onPressed: () async { | |
await ref.read(paintingServiceProvider).validatePicture(); | |
}, | |
icon: const Icon(Icons.image), | |
label: const Text('Validate Picture') | |
), | |
const SizedBox(width: 24), | |
ElevatedButton.icon( | |
icon: const Icon(Icons.clear), | |
onPressed: () async { | |
ref.read(paintingServiceProvider).clear(); | |
}, | |
label: const Text('Clear') | |
) | |
], | |
), | |
); | |
} | |
} | |
class PaintingValidator extends ConsumerWidget { | |
const PaintingValidator({super.key}); | |
@override | |
Widget build(BuildContext context, WidgetRef ref) { | |
final imgPayload = ref.watch(imageResultNotifier); | |
if (imgPayload == null) { | |
return const SizedBox.shrink(); | |
} | |
return Container( | |
margin: const EdgeInsets.all(20), | |
padding: const EdgeInsets.all(20), | |
decoration: BoxDecoration( | |
color: Colors.white, | |
borderRadius: BorderRadius.circular(20), | |
), | |
child: Row( | |
children: [ | |
Image.memory(imgPayload.imgBytes, | |
width: 200, | |
height: 200, | |
), | |
const SizedBox(width: 24), | |
Expanded( | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
const Text('Guessed Image:', style: TextStyle(fontSize: 20, color: Colors.purpleAccent),), | |
Text(imgPayload.name, style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),), | |
], | |
), | |
) | |
], | |
), | |
).animate() | |
.slideY( | |
begin: 1, end: 0, | |
curve: Curves.easeInOut, | |
duration: 0.5.seconds, | |
); | |
} | |
} | |
class PaintingSurface extends ConsumerStatefulWidget { | |
const PaintingSurface({super.key}); | |
@override | |
ConsumerState<PaintingSurface> createState() => _PaintingSurfaceState(); | |
} | |
class _PaintingSurfaceState extends ConsumerState<PaintingSurface> { | |
late PainterController painterController; | |
@override | |
void initState() { | |
super.initState(); | |
initializePainterController(); | |
} | |
void initializePainterController() async { | |
painterController = ref.read(painterControllerProvider); | |
ByteData bgImg = await rootBundle.load('assets/bgimg.png'); | |
painterController.setBackgroundImage(bgImg.buffer.asUint8List()); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Painter( | |
controller: painterController, | |
backgroundColor: Colors.white, | |
size: Size( | |
MediaQuery.sizeOf(context).width, 300, | |
), | |
child: const ColoredBox(color: Colors.white), | |
); | |
} | |
} | |
final painterControllerProvider = Provider<PainterController>((ref) { | |
return PainterController() | |
..setPenType(PenType.paintbrush) | |
..setStrokeColor(Colors.black) | |
..setMinStrokeWidth(3) | |
..setMaxStrokeWidth(10) | |
..setBlurSigma(0.0) | |
..setBlendMode(ui.BlendMode.srcOver); | |
}); | |
final paintingServiceProvider = Provider<GeminiPaintingService>((ref) { | |
return GeminiPaintingService(ref); | |
}); | |
final imageResultNotifier = StateProvider<ImageResultPayload?>((ref) => null); | |
class GeminiPaintingService { | |
final Ref ref; | |
const GeminiPaintingService(this.ref); | |
Future<void> validatePicture() async { | |
try { | |
Uint8List imgBytes = ref.read(painterControllerProvider).getImageBytes()!; | |
var model = GenerativeModel( | |
model: 'gemini-1.5-pro', | |
apiKey: 'YOUR_API_KEY_HERE', // <- ADD YOUR API KEY HERE | |
generationConfig: GenerationConfig( | |
responseMimeType: 'application/json', | |
responseSchema: Schema.object( | |
properties: { | |
"guessedImage": Schema.string(), | |
} | |
) | |
) | |
); | |
final content = Content.multi([ | |
TextPart(''' | |
You are a pictionary player. Given the provided image, identify its contents | |
and reply in the form of a JSON payload response containing the following properties: | |
{ | |
"guessedImage": the name of the image drawn | |
} | |
'''), | |
DataPart('image/png', imgBytes) | |
]); | |
var response = await model.generateContent([content]); | |
var jsonPayload = json.decode(response.text!); | |
ref.read(imageResultNotifier.notifier).state = | |
ImageResultPayload( | |
name: jsonPayload['guessedImage'], | |
imgBytes: imgBytes | |
); | |
} | |
on Exception { | |
rethrow; | |
} | |
} | |
void clear() { | |
ref.read(painterControllerProvider).clearContent(clearColor: Colors.white); | |
ref.read(imageResultNotifier.notifier).state = null; | |
} | |
} | |
class ImageResultPayload { | |
final String name; | |
final Uint8List imgBytes; | |
const ImageResultPayload({ | |
required this.name, | |
required this.imgBytes, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment