Skip to content

chore(vertexai): update sample for developer api and fix the instance key for FirebaseVertexAI #17319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ app.*.map.json
firebase_options.dart
google-services.json
GoogleService-Info.plist
firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
}

Expand Down
290 changes: 206 additions & 84 deletions packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,161 +38,283 @@ void main() async {
// await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await Firebase.initializeApp();
await FirebaseAuth.instance.signInAnonymously();
runApp(const GenerativeAISample());
}

var vertexInstance =
FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance);
final model = vertexInstance.generativeModel(model: 'gemini-1.5-flash');
class GenerativeAISample extends StatefulWidget {
const GenerativeAISample({super.key});

runApp(GenerativeAISample(model: model));
@override
State<GenerativeAISample> createState() => _GenerativeAISampleState();
}

class GenerativeAISample extends StatelessWidget {
final GenerativeModel model;
class _GenerativeAISampleState extends State<GenerativeAISample> {
bool _useVertexBackend = false;
late GenerativeModel _currentModel;
late ImagenModel _currentImagenModel;
int _currentBottomNavIndex = 0;

@override
void initState() {
super.initState();

_initializeModel(_useVertexBackend);
}

const GenerativeAISample({super.key, required this.model});
void _initializeModel(bool useVertexBackend) {
if (useVertexBackend) {
final vertexInstance =
FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance);
_currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash');
_currentImagenModel = _initializeImagenModel(vertexInstance);
} else {
final googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance);
_currentModel = googleAI.generativeModel(model: 'gemini-2.0-flash');
_currentImagenModel = _initializeImagenModel(googleAI);
}
}

ImagenModel _initializeImagenModel(FirebaseVertexAI instance) {
var generationConfig = ImagenGenerationConfig(
negativePrompt: 'frog',
numberOfImages: 1,
aspectRatio: ImagenAspectRatio.square1x1,
imageFormat: ImagenFormat.jpeg(compressionQuality: 75),
);
return instance.imagenModel(
model: 'imagen-3.0-generate-001',
generationConfig: generationConfig,
safetySettings: ImagenSafetySettings(
ImagenSafetyFilterLevel.blockLowAndAbove,
ImagenPersonFilterLevel.allowAdult,
),
);
}

void _toggleBackend(bool value) {
setState(() {
_useVertexBackend = value;
});
_initializeModel(_useVertexBackend);
}

void _onBottomNavTapped(int index) {
setState(() {
_currentBottomNavIndex = index;
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter + Vertex AI',
title: 'Flutter + ${_useVertexBackend ? 'Vertex AI' : 'Google AI'}',
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
brightness: Brightness.dark,
seedColor: const Color.fromARGB(255, 171, 222, 244),
),
useMaterial3: true,
),
home: HomeScreen(model: model),
home: HomeScreen(
key: ValueKey(
'${_useVertexBackend}_${_currentModel.hashCode}',
),
model: _currentModel,
imagenModel: _currentImagenModel,
useVertexBackend: _useVertexBackend,
onBackendChanged: _toggleBackend,
selectedIndex: _currentBottomNavIndex,
onSelectedIndexChanged: _onBottomNavTapped,
),
);
}
}

class HomeScreen extends StatefulWidget {
final GenerativeModel model;
const HomeScreen({super.key, required this.model});
final ImagenModel imagenModel;
final bool useVertexBackend;
final ValueChanged<bool> onBackendChanged;
final int selectedIndex;
final ValueChanged<int> onSelectedIndexChanged;

const HomeScreen({
super.key,
required this.model,
required this.imagenModel,
required this.useVertexBackend,
required this.onBackendChanged,
required this.selectedIndex,
required this.onSelectedIndexChanged,
});

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;

List<Widget> get _pages => <Widget>[
// Build _pages dynamically
ChatPage(title: 'Chat', model: widget.model),
AudioPage(title: 'Audio', model: widget.model),
TokenCountPage(title: 'Token Count', model: widget.model),
const FunctionCallingPage(
title: 'Function Calling',
), // function calling will initial its own model
ImagePromptPage(title: 'Image Prompt', model: widget.model),
ImagenPage(title: 'Imagen Model', model: widget.model),
SchemaPromptPage(title: 'Schema Prompt', model: widget.model),
DocumentPage(title: 'Document Prompt', model: widget.model),
VideoPage(title: 'Video Prompt', model: widget.model),
BidiPage(title: 'Bidi Stream', model: widget.model),
];

void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
widget.onSelectedIndexChanged(index);
}

// Method to build the selected page on demand
Widget _buildSelectedPage(
int index,
GenerativeModel currentModel,
ImagenModel currentImagenModel,
bool useVertexBackend,
) {
switch (index) {
case 0:
return ChatPage(title: 'Chat', model: currentModel);
case 1:
return AudioPage(title: 'Audio', model: currentModel);
case 2:
return TokenCountPage(title: 'Token Count', model: currentModel);
case 3:
// FunctionCallingPage initializes its own model as per original design
return FunctionCallingPage(
title: 'Function Calling',
useVertexBackend: useVertexBackend,
);
case 4:
return ImagePromptPage(title: 'Image Prompt', model: currentModel);
case 5:
return ImagenPage(title: 'Imagen Model', model: currentImagenModel);
case 6:
return SchemaPromptPage(title: 'Schema Prompt', model: currentModel);
case 7:
return DocumentPage(title: 'Document Prompt', model: currentModel);
case 8:
return VideoPage(title: 'Video Prompt', model: currentModel);
case 9:
return BidiPage(title: 'Bidi Stream', model: currentModel);
default:
// Fallback to the first page in case of an unexpected index
return ChatPage(title: 'Chat', model: currentModel);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter + Vertex AI'),
title: Text(
'Flutter + ${widget.useVertexBackend ? 'Vertex AI' : 'Google AI'}',
),
actions: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Google AI',
style: TextStyle(
fontSize: 12,
color: widget.useVertexBackend
? Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.7)
: Theme.of(context).colorScheme.primary,
),
),
Switch(
value: widget.useVertexBackend,
onChanged: widget.onBackendChanged,
activeTrackColor: Colors.green.withValues(alpha: 0.5),
inactiveTrackColor: Colors.blueGrey.withValues(alpha: 0.5),
activeColor: Colors.green,
inactiveThumbColor: Colors.blueGrey,
),
Text(
'Vertex AI',
style: TextStyle(
fontSize: 12,
color: widget.useVertexBackend
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.7),
),
),
],
),
),
],
),
body: Center(
child: _pages.elementAt(_selectedIndex),
child: _buildSelectedPage(
widget.selectedIndex,
widget.model,
widget.imagenModel,
widget.useVertexBackend,
),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
type: BottomNavigationBarType.fixed,
selectedFontSize: 10,
unselectedFontSize: 9,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor:
Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.chat,
color: Theme.of(context).colorScheme.primary,
),
icon: Icon(Icons.chat),
label: 'Chat',
tooltip: 'Chat',
),
BottomNavigationBarItem(
icon: Icon(
Icons.mic,
color: Theme.of(context).colorScheme.primary,
),
label: 'Audio Prompt',
icon: Icon(Icons.mic),
label: 'Audio',
tooltip: 'Audio Prompt',
),
BottomNavigationBarItem(
icon: Icon(
Icons.numbers,
color: Theme.of(context).colorScheme.primary,
),
label: 'Token Count',
icon: Icon(Icons.numbers),
label: 'Tokens',
tooltip: 'Token Count',
),
BottomNavigationBarItem(
icon: Icon(
Icons.functions,
color: Theme.of(context).colorScheme.primary,
),
label: 'Function Calling',
icon: Icon(Icons.functions),
label: 'Functions',
tooltip: 'Function Calling',
),
BottomNavigationBarItem(
icon: Icon(
Icons.image,
color: Theme.of(context).colorScheme.primary,
),
label: 'Image Prompt',
icon: Icon(Icons.image),
label: 'Image',
tooltip: 'Image Prompt',
),
BottomNavigationBarItem(
icon: Icon(
Icons.image_search,
color: Theme.of(context).colorScheme.primary,
),
label: 'Imagen Model',
icon: Icon(Icons.image_search),
label: 'Imagen',
tooltip: 'Imagen Model',
),
BottomNavigationBarItem(
icon: Icon(
Icons.schema,
color: Theme.of(context).colorScheme.primary,
),
label: 'Schema Prompt',
icon: Icon(Icons.schema),
label: 'Schema',
tooltip: 'Schema Prompt',
),
BottomNavigationBarItem(
icon: Icon(
Icons.edit_document,
color: Theme.of(context).colorScheme.primary,
),
label: 'Document Prompt',
icon: Icon(Icons.edit_document),
label: 'Document',
tooltip: 'Document Prompt',
),
BottomNavigationBarItem(
icon: Icon(
Icons.video_collection,
color: Theme.of(context).colorScheme.primary,
),
label: 'Video Prompt',
icon: Icon(Icons.video_collection),
label: 'Video',
tooltip: 'Video Prompt',
),
BottomNavigationBarItem(
icon: Icon(
Icons.stream,
color: Theme.of(context).colorScheme.primary,
),
label: 'Bidi Stream',
icon: Icon(Icons.stream),
label: 'Bidi',
tooltip: 'Bidi Stream',
),
],
currentIndex: _selectedIndex,
currentIndex: widget.selectedIndex,
onTap: _onItemTapped,
),
);
Expand Down
Loading
Loading