import 'package:google_generative_ai/google_generative_ai.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FlutterAIChat extends ConsumerStatefulWidget {
const FlutterAIChat({super.key});
@override
ConsumerState<FlutterAIChat> createState() => _State();
}
class _State extends ConsumerState<FlutterAIChat> {
final List<Content> _history = [];
late final ChatSession _chat;
late final GenerativeModel _model;
@override
void initState() {
super.initState();
_model = GenerativeModel(
model: 'gemini-1.5-pro',
apiKey: const String.fromEnvironment('GEMINI_KEY'),
generationConfig: GenerationConfig(
temperature: 0.7,
topP: 0.95,
maxOutputTokens: 2048,
),
);
_chat = _model.startChat(history: _history);
}
Future<void> _send(String text) async {
final resp = await _chat.sendMessage(Content.text(text));
setState(() {
_history.add(Content.model([TextPart(resp.text ?? '')]));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _history.length,
itemBuilder: (_, i) => MessageBubble(_history[i]),
),
),
AIInputBar(onSubmit: _send),
],
),
);
}
}
import 'package:google_generative_ai/google_generative_ai.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FlutterAIChat extends ConsumerStatefulWidget {
const FlutterAIChat({super.key});
@override
ConsumerState<FlutterAIChat> createState() => _State();
}
class _State extends ConsumerState<FlutterAIChat> {
final List<Content> _history = [];
late final ChatSession _chat;
late final GenerativeModel _model;
@override
void initState() {
super.initState();
_model = GenerativeModel(
model: 'gemini-1.5-pro',
apiKey: const String.fromEnvironment('GEMINI_KEY'),
generationConfig: GenerationConfig(
temperature: 0.7,
topP: 0.95,
maxOutputTokens: 2048,
),
);
_chat = _model.startChat(history: _history);
}
Future<void> _send(String text) async {
final resp = await _chat.sendMessage(Content.text(text));
setState(() {
_history.add(Content.model([TextPart(resp.text ?? '')]));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _history.length,
itemBuilder: (_, i) => MessageBubble(_history[i]),
),
),
AIInputBar(onSubmit: _send),
],
),
);
}
}
import 'package:google_generative_ai/google_generative_ai.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FlutterAIChat extends ConsumerStatefulWidget {
const FlutterAIChat({super.key});
@override
ConsumerState<FlutterAIChat> createState() => _State();
}
class _State extends ConsumerState<FlutterAIChat> {
final List<Content> _history = [];
late final ChatSession _chat;
late final GenerativeModel _model;
@override
void initState() {
super.initState();
_model = GenerativeModel(
model: 'gemini-1.5-pro',
apiKey: const String.fromEnvironment('GEMINI_KEY'),
generationConfig: GenerationConfig(
temperature: 0.7,
topP: 0.95,
maxOutputTokens: 2048,
),
);
_chat = _model.startChat(history: _history);
}
Future<void> _send(String text) async {
final resp = await _chat.sendMessage(Content.text(text));
setState(() {
_history.add(Content.model([TextPart(resp.text ?? '')]));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _history.length,
itemBuilder: (_, i) => MessageBubble(_history[i]),
),
),
AIInputBar(onSubmit: _send),
],
),
);
}
}import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
import 'package:camera/camera.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'vision_ai.g.dart';
@riverpod
class VisionAINotifier extends _$VisionAINotifier {
late final ObjectDetector _detector;
@override
AsyncValue<List<DetectedObject>> build() {
_detector = ObjectDetector(
options: ObjectDetectorOptions(
mode: DetectionMode.stream,
classifyObjects: true,
multipleObjects: true,
),
);
ref.onDispose(_detector.close);
return const AsyncValue.data([]);
}
Future<void> processFrame(CameraImage frame) async {
final image = InputImage.fromBytes(
bytes: frame.planes.first.bytes,
metadata: InputImageMetadata(
size: Size(frame.width.toDouble(), frame.height.toDouble()),
rotation: InputImageRotation.rotation0deg,
format: InputImageFormat.bgra8888,
bytesPerRow: frame.planes.first.bytesPerRow,
),
);
final objects = await _detector.processImage(image);
state = AsyncValue.data(objects);
}
}
class AROverlayPainter extends CustomPainter {
final List<DetectedObject> objects;
AROverlayPainter(this.objects);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.cyanAccent.withOpacity(0.8)
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
for (final obj in objects) {
canvas.drawRRect(
RRect.fromRectAndRadius(obj.boundingBox, const Radius.circular(8)),
paint,
);
for (final label in obj.labels) {
_drawLabel(canvas, label.text, obj.boundingBox.topLeft);
}
}
}
@override
bool shouldRepaint(AROverlayPainter old) => old.objects != objects;
}
import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
import 'package:camera/camera.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'vision_ai.g.dart';
@riverpod
class VisionAINotifier extends _$VisionAINotifier {
late final ObjectDetector _detector;
@override
AsyncValue<List<DetectedObject>> build() {
_detector = ObjectDetector(
options: ObjectDetectorOptions(
mode: DetectionMode.stream,
classifyObjects: true,
multipleObjects: true,
),
);
ref.onDispose(_detector.close);
return const AsyncValue.data([]);
}
Future<void> processFrame(CameraImage frame) async {
final image = InputImage.fromBytes(
bytes: frame.planes.first.bytes,
metadata: InputImageMetadata(
size: Size(frame.width.toDouble(), frame.height.toDouble()),
rotation: InputImageRotation.rotation0deg,
format: InputImageFormat.bgra8888,
bytesPerRow: frame.planes.first.bytesPerRow,
),
);
final objects = await _detector.processImage(image);
state = AsyncValue.data(objects);
}
}
class AROverlayPainter extends CustomPainter {
final List<DetectedObject> objects;
AROverlayPainter(this.objects);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.cyanAccent.withOpacity(0.8)
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
for (final obj in objects) {
canvas.drawRRect(
RRect.fromRectAndRadius(obj.boundingBox, const Radius.circular(8)),
paint,
);
for (final label in obj.labels) {
_drawLabel(canvas, label.text, obj.boundingBox.topLeft);
}
}
}
@override
bool shouldRepaint(AROverlayPainter old) => old.objects != objects;
}
import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
import 'package:camera/camera.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'vision_ai.g.dart';
@riverpod
class VisionAINotifier extends _$VisionAINotifier {
late final ObjectDetector _detector;
@override
AsyncValue<List<DetectedObject>> build() {
_detector = ObjectDetector(
options: ObjectDetectorOptions(
mode: DetectionMode.stream,
classifyObjects: true,
multipleObjects: true,
),
);
ref.onDispose(_detector.close);
return const AsyncValue.data([]);
}
Future<void> processFrame(CameraImage frame) async {
final image = InputImage.fromBytes(
bytes: frame.planes.first.bytes,
metadata: InputImageMetadata(
size: Size(frame.width.toDouble(), frame.height.toDouble()),
rotation: InputImageRotation.rotation0deg,
format: InputImageFormat.bgra8888,
bytesPerRow: frame.planes.first.bytesPerRow,
),
);
final objects = await _detector.processImage(image);
state = AsyncValue.data(objects);
}
}
class AROverlayPainter extends CustomPainter {
final List<DetectedObject> objects;
AROverlayPainter(this.objects);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.cyanAccent.withOpacity(0.8)
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
for (final obj in objects) {
canvas.drawRRect(
RRect.fromRectAndRadius(obj.boundingBox, const Radius.circular(8)),
paint,
);
for (final label in obj.labels) {
_drawLabel(canvas, label.text, obj.boundingBox.topLeft);
}
}
}
@override
bool shouldRepaint(AROverlayPainter old) => old.objects != objects;
}import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:uuid/uuid.dart';
part 'ai_state.freezed.dart';
part 'ai_state.g.dart';
@freezed
class AIResponse with _$AIResponse {
const factory AIResponse({
required String id,
required String text,
required DateTime createdAt,
@Default([]) List<String> citations,
@Default(0.0) double confidence,
@Default(AIModel.geminiPro) AIModel model,
}) = _AIResponse;
factory AIResponse.fromJson(Map<String, dynamic> j) =>
_$AIResponseFromJson(j);
}
@riverpod
class AIStateNotifier extends _$AIStateNotifier {
@override
AsyncValue<AIResponse?> build() => const AsyncValue.data(null);
Future<void> generate({
required String prompt,
AIModel model = AIModel.geminiPro,
}) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final gemini = ref.read(geminiServiceProvider);
final result = await gemini.generateContent(
contents: [Content.text(prompt)],
config: GenerationConfig(
temperature: 0.8,
responseMimeType: 'application/json',
),
);
return AIResponse(
id: const Uuid().v4(),
text: result.text ?? '',
createdAt: DateTime.now(),
model: model,
confidence: _computeConfidence(result),
);
});
}
double _computeConfidence(GenerateContentResponse r) =>
r.candidates.first.safetyRatings
.map((s) => s.probability.index / 5.0)
.reduce((a, b) => (a + b) / 2);
}
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:uuid/uuid.dart';
part 'ai_state.freezed.dart';
part 'ai_state.g.dart';
@freezed
class AIResponse with _$AIResponse {
const factory AIResponse({
required String id,
required String text,
required DateTime createdAt,
@Default([]) List<String> citations,
@Default(0.0) double confidence,
@Default(AIModel.geminiPro) AIModel model,
}) = _AIResponse;
factory AIResponse.fromJson(Map<String, dynamic> j) =>
_$AIResponseFromJson(j);
}
@riverpod
class AIStateNotifier extends _$AIStateNotifier {
@override
AsyncValue<AIResponse?> build() => const AsyncValue.data(null);
Future<void> generate({
required String prompt,
AIModel model = AIModel.geminiPro,
}) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final gemini = ref.read(geminiServiceProvider);
final result = await gemini.generateContent(
contents: [Content.text(prompt)],
config: GenerationConfig(
temperature: 0.8,
responseMimeType: 'application/json',
),
);
return AIResponse(
id: const Uuid().v4(),
text: result.text ?? '',
createdAt: DateTime.now(),
model: model,
confidence: _computeConfidence(result),
);
});
}
double _computeConfidence(GenerateContentResponse r) =>
r.candidates.first.safetyRatings
.map((s) => s.probability.index / 5.0)
.reduce((a, b) => (a + b) / 2);
}
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:uuid/uuid.dart';
part 'ai_state.freezed.dart';
part 'ai_state.g.dart';
@freezed
class AIResponse with _$AIResponse {
const factory AIResponse({
required String id,
required String text,
required DateTime createdAt,
@Default([]) List<String> citations,
@Default(0.0) double confidence,
@Default(AIModel.geminiPro) AIModel model,
}) = _AIResponse;
factory AIResponse.fromJson(Map<String, dynamic> j) =>
_$AIResponseFromJson(j);
}
@riverpod
class AIStateNotifier extends _$AIStateNotifier {
@override
AsyncValue<AIResponse?> build() => const AsyncValue.data(null);
Future<void> generate({
required String prompt,
AIModel model = AIModel.geminiPro,
}) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final gemini = ref.read(geminiServiceProvider);
final result = await gemini.generateContent(
contents: [Content.text(prompt)],
config: GenerationConfig(
temperature: 0.8,
responseMimeType: 'application/json',
),
);
return AIResponse(
id: const Uuid().v4(),
text: result.text ?? '',
createdAt: DateTime.now(),
model: model,
confidence: _computeConfidence(result),
);
});
}
double _computeConfidence(GenerateContentResponse r) =>
r.candidates.first.safetyRatings
.map((s) => s.probability.index / 5.0)
.reduce((a, b) => (a + b) / 2);
}BecomingA FlutterAI Engineer
Join 600+ developers at the 5th edition of the largest Flutter Conference in Africa.
5 Years, 1 Community:Continuous Growth and Impact
From a WhatsApp group for Flutter devs in Lagos to Africa's biggest Flutter conference — this is our story.
What started as a WhatsApp group for Flutter devs in Nigeria quickly turned into something none of us expected...
The story continues...
The community keeps shipping. The next chapter is yours.
— The Event
Friday & Saturday
October 30th & 31st, 2026
Countdown
Zone Tech Park
Off Gbagada Express, Gbagada, Lagos
What's on the schedule
A line up of sessions that will literally equip you to become A Flutter AI engineer.
Building Scalable Applications Using Flutter
Atuoha Anthony
Writing True Mobile Tests with Patrol
Sebastine Odeh
In-App Subscription: Monetizing Your Flutter Apps
Odinachi David
Building Offline AI Agent in Your Flutter App
Sasha Denisov
The Future of Real-Time UX: Live Activities in Flutter for Android & iOS
Kudus Rufai
DCM for Clean, Consistent Flutter Code
Festus Olusegun
Building the Bridge: Running JavaScript Modules from Dart
Chima Precious
Build Spectacular TV Apps with Flutter
Mrinal Jain
Building an AI Agent to Manage Your Flutter/Dart Dependencies
Agalaba Ifeanyi
Data-Driven Design with Flutter: Improving UX Based on User Behavior
Nikki Eke
Going Native: Building a Flutter Step Counter Plugin with Kotlin & Jetpack Compose
Glory Olaifa
From Burnout to Breakthrough: Sustainable Development Practices for Flutter Developers
David Oluwabusayo
Building an Offline Running Game with Flutter
Ayomiposi Fabiyi
Never Break Your UI Again: Visual Testing with Widgetbook Cloud
Jesutoni Aderibigbe
Crafting Mobile Apps with Customer Experience in Mind
Temitayo Olakunle
The Power of a Design System: Building Consistent UI with Flutter
Samuel Abada
Managing Multi-Package Flutter Projects in a Monorepo Using Melos
Caleb Jesusegun
Bringing Generative AI to Flutter: Build a Voice-Enabled AI Assistant with ElevenLabs + Dart
Emmanuel Akinfulubi
Building Pixel-Perfect UIs in Flutter
Sodiq Eniola
The Modern Flutter Aesthetic: Crafting Custom Experiences with Fragment Shaders
Yunwen Eric
No More Leaks: Detect & Prevent Memory Leaks
Michael Ogundipe
Dart on the Server: Building Scalable APIs with Serverpod and Flutter
Samuel Adekunle
Speeding Up Your Flutter Development with AI Tools
Ayodeji Michael
Building Agentic Apps with Flutter and Firebase Genkit
Hassan Bahati
FlutterBytes Speakers so far…
Engineers, founders, and Flutter enthusiasts who've taken the stage across all editions.








Odinachi David
Mobile Engineer
HealaTech
Wheel spins in 5s
What happens at FlutterBytes
doesn't end at FlutterBytes
The impact of each edition always outlasts the event day, and the evidence abounds.
2025


2024


2023


2022


FlutterBytes sponsors
FlutterBytes Conference has been sponsored by some of the top companies in Africa and the world — companies that invest in developers because they know it's the best bet.
Partner with us
Become a sponsor
Reach 600+ Flutter engineers, founders and tech leaders at Africa's premier mobile conference.
The Flutter Bytes
The people with the highest commits to the FlutterBytes Conferences
No matter what, AI can't replace or replicate the efforts of the organising team












Are you ready for the next level?
Join 600+ Flutter Devs this October. Don't miss out on a rare opportunity for incredible growth.














