@@ -38,161 +38,283 @@ void main() async {
3838 // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
3939 await Firebase .initializeApp ();
4040 await FirebaseAuth .instance.signInAnonymously ();
41+ runApp (const GenerativeAISample ());
42+ }
4143
42- var vertexInstance =
43- FirebaseVertexAI .instanceFor (auth: FirebaseAuth .instance);
44- final model = vertexInstance.generativeModel (model: 'gemini-1.5-flash' );
44+ class GenerativeAISample extends StatefulWidget {
45+ const GenerativeAISample ({super .key});
4546
46- runApp (GenerativeAISample (model: model));
47+ @override
48+ State <GenerativeAISample > createState () => _GenerativeAISampleState ();
4749}
4850
49- class GenerativeAISample extends StatelessWidget {
50- final GenerativeModel model;
51+ class _GenerativeAISampleState extends State <GenerativeAISample > {
52+ bool _useVertexBackend = false ;
53+ late GenerativeModel _currentModel;
54+ late ImagenModel _currentImagenModel;
55+ int _currentBottomNavIndex = 0 ;
56+
57+ @override
58+ void initState () {
59+ super .initState ();
60+
61+ _initializeModel (_useVertexBackend);
62+ }
5163
52- const GenerativeAISample ({super .key, required this .model});
64+ void _initializeModel (bool useVertexBackend) {
65+ if (useVertexBackend) {
66+ final vertexInstance =
67+ FirebaseVertexAI .instanceFor (auth: FirebaseAuth .instance);
68+ _currentModel = vertexInstance.generativeModel (model: 'gemini-1.5-flash' );
69+ _currentImagenModel = _initializeImagenModel (vertexInstance);
70+ } else {
71+ final googleAI = FirebaseVertexAI .googleAI (auth: FirebaseAuth .instance);
72+ _currentModel = googleAI.generativeModel (model: 'gemini-2.0-flash' );
73+ _currentImagenModel = _initializeImagenModel (googleAI);
74+ }
75+ }
76+
77+ ImagenModel _initializeImagenModel (FirebaseVertexAI instance) {
78+ var generationConfig = ImagenGenerationConfig (
79+ negativePrompt: 'frog' ,
80+ numberOfImages: 1 ,
81+ aspectRatio: ImagenAspectRatio .square1x1,
82+ imageFormat: ImagenFormat .jpeg (compressionQuality: 75 ),
83+ );
84+ return instance.imagenModel (
85+ model: 'imagen-3.0-generate-001' ,
86+ generationConfig: generationConfig,
87+ safetySettings: ImagenSafetySettings (
88+ ImagenSafetyFilterLevel .blockLowAndAbove,
89+ ImagenPersonFilterLevel .allowAdult,
90+ ),
91+ );
92+ }
93+
94+ void _toggleBackend (bool value) {
95+ setState (() {
96+ _useVertexBackend = value;
97+ });
98+ _initializeModel (_useVertexBackend);
99+ }
100+
101+ void _onBottomNavTapped (int index) {
102+ setState (() {
103+ _currentBottomNavIndex = index;
104+ });
105+ }
53106
54107 @override
55108 Widget build (BuildContext context) {
56109 return MaterialApp (
57- title: 'Flutter + Vertex AI' ,
110+ title: 'Flutter + ${_useVertexBackend ? 'Vertex AI' : 'Google AI' }' ,
111+ debugShowCheckedModeBanner: false ,
112+ themeMode: ThemeMode .dark,
58113 theme: ThemeData (
59114 colorScheme: ColorScheme .fromSeed (
60115 brightness: Brightness .dark,
61116 seedColor: const Color .fromARGB (255 , 171 , 222 , 244 ),
62117 ),
63118 useMaterial3: true ,
64119 ),
65- home: HomeScreen (model: model),
120+ home: HomeScreen (
121+ key: ValueKey (
122+ '${_useVertexBackend }_${_currentModel .hashCode }' ,
123+ ),
124+ model: _currentModel,
125+ imagenModel: _currentImagenModel,
126+ useVertexBackend: _useVertexBackend,
127+ onBackendChanged: _toggleBackend,
128+ selectedIndex: _currentBottomNavIndex,
129+ onSelectedIndexChanged: _onBottomNavTapped,
130+ ),
66131 );
67132 }
68133}
69134
70135class HomeScreen extends StatefulWidget {
71136 final GenerativeModel model;
72- const HomeScreen ({super .key, required this .model});
137+ final ImagenModel imagenModel;
138+ final bool useVertexBackend;
139+ final ValueChanged <bool > onBackendChanged;
140+ final int selectedIndex;
141+ final ValueChanged <int > onSelectedIndexChanged;
142+
143+ const HomeScreen ({
144+ super .key,
145+ required this .model,
146+ required this .imagenModel,
147+ required this .useVertexBackend,
148+ required this .onBackendChanged,
149+ required this .selectedIndex,
150+ required this .onSelectedIndexChanged,
151+ });
73152
74153 @override
75154 State <HomeScreen > createState () => _HomeScreenState ();
76155}
77156
78157class _HomeScreenState extends State <HomeScreen > {
79- int _selectedIndex = 0 ;
80-
81- List <Widget > get _pages => < Widget > [
82- // Build _pages dynamically
83- ChatPage (title: 'Chat' , model: widget.model),
84- AudioPage (title: 'Audio' , model: widget.model),
85- TokenCountPage (title: 'Token Count' , model: widget.model),
86- const FunctionCallingPage (
87- title: 'Function Calling' ,
88- ), // function calling will initial its own model
89- ImagePromptPage (title: 'Image Prompt' , model: widget.model),
90- ImagenPage (title: 'Imagen Model' , model: widget.model),
91- SchemaPromptPage (title: 'Schema Prompt' , model: widget.model),
92- DocumentPage (title: 'Document Prompt' , model: widget.model),
93- VideoPage (title: 'Video Prompt' , model: widget.model),
94- BidiPage (title: 'Bidi Stream' , model: widget.model),
95- ];
96-
97158 void _onItemTapped (int index) {
98- setState (() {
99- _selectedIndex = index;
100- });
159+ widget.onSelectedIndexChanged (index);
160+ }
161+
162+ // Method to build the selected page on demand
163+ Widget _buildSelectedPage (
164+ int index,
165+ GenerativeModel currentModel,
166+ ImagenModel currentImagenModel,
167+ bool useVertexBackend,
168+ ) {
169+ switch (index) {
170+ case 0 :
171+ return ChatPage (title: 'Chat' , model: currentModel);
172+ case 1 :
173+ return AudioPage (title: 'Audio' , model: currentModel);
174+ case 2 :
175+ return TokenCountPage (title: 'Token Count' , model: currentModel);
176+ case 3 :
177+ // FunctionCallingPage initializes its own model as per original design
178+ return FunctionCallingPage (
179+ title: 'Function Calling' ,
180+ useVertexBackend: useVertexBackend,
181+ );
182+ case 4 :
183+ return ImagePromptPage (title: 'Image Prompt' , model: currentModel);
184+ case 5 :
185+ return ImagenPage (title: 'Imagen Model' , model: currentImagenModel);
186+ case 6 :
187+ return SchemaPromptPage (title: 'Schema Prompt' , model: currentModel);
188+ case 7 :
189+ return DocumentPage (title: 'Document Prompt' , model: currentModel);
190+ case 8 :
191+ return VideoPage (title: 'Video Prompt' , model: currentModel);
192+ case 9 :
193+ return BidiPage (title: 'Bidi Stream' , model: currentModel);
194+ default :
195+ // Fallback to the first page in case of an unexpected index
196+ return ChatPage (title: 'Chat' , model: currentModel);
197+ }
101198 }
102199
103200 @override
104201 Widget build (BuildContext context) {
105202 return Scaffold (
106203 appBar: AppBar (
107- title: const Text ('Flutter + Vertex AI' ),
204+ title: Text (
205+ 'Flutter + ${widget .useVertexBackend ? 'Vertex AI' : 'Google AI' }' ,
206+ ),
207+ actions: < Widget > [
208+ Padding (
209+ padding: const EdgeInsets .symmetric (horizontal: 16 ),
210+ child: Row (
211+ mainAxisSize: MainAxisSize .min,
212+ children: < Widget > [
213+ Text (
214+ 'Google AI' ,
215+ style: TextStyle (
216+ fontSize: 12 ,
217+ color: widget.useVertexBackend
218+ ? Theme .of (context)
219+ .colorScheme
220+ .onSurface
221+ .withValues (alpha: 0.7 )
222+ : Theme .of (context).colorScheme.primary,
223+ ),
224+ ),
225+ Switch (
226+ value: widget.useVertexBackend,
227+ onChanged: widget.onBackendChanged,
228+ activeTrackColor: Colors .green.withValues (alpha: 0.5 ),
229+ inactiveTrackColor: Colors .blueGrey.withValues (alpha: 0.5 ),
230+ activeColor: Colors .green,
231+ inactiveThumbColor: Colors .blueGrey,
232+ ),
233+ Text (
234+ 'Vertex AI' ,
235+ style: TextStyle (
236+ fontSize: 12 ,
237+ color: widget.useVertexBackend
238+ ? Theme .of (context).colorScheme.primary
239+ : Theme .of (context)
240+ .colorScheme
241+ .onSurface
242+ .withValues (alpha: 0.7 ),
243+ ),
244+ ),
245+ ],
246+ ),
247+ ),
248+ ],
108249 ),
109250 body: Center (
110- child: _pages.elementAt (_selectedIndex),
251+ child: _buildSelectedPage (
252+ widget.selectedIndex,
253+ widget.model,
254+ widget.imagenModel,
255+ widget.useVertexBackend,
256+ ),
111257 ),
112258 bottomNavigationBar: BottomNavigationBar (
113- items: < BottomNavigationBarItem > [
259+ type: BottomNavigationBarType .fixed,
260+ selectedFontSize: 10 ,
261+ unselectedFontSize: 9 ,
262+ selectedItemColor: Theme .of (context).colorScheme.primary,
263+ unselectedItemColor:
264+ Theme .of (context).colorScheme.onSurface.withValues (alpha: 0.7 ),
265+ items: const < BottomNavigationBarItem > [
114266 BottomNavigationBarItem (
115- icon: Icon (
116- Icons .chat,
117- color: Theme .of (context).colorScheme.primary,
118- ),
267+ icon: Icon (Icons .chat),
119268 label: 'Chat' ,
120269 tooltip: 'Chat' ,
121270 ),
122271 BottomNavigationBarItem (
123- icon: Icon (
124- Icons .mic,
125- color: Theme .of (context).colorScheme.primary,
126- ),
127- label: 'Audio Prompt' ,
272+ icon: Icon (Icons .mic),
273+ label: 'Audio' ,
128274 tooltip: 'Audio Prompt' ,
129275 ),
130276 BottomNavigationBarItem (
131- icon: Icon (
132- Icons .numbers,
133- color: Theme .of (context).colorScheme.primary,
134- ),
135- label: 'Token Count' ,
277+ icon: Icon (Icons .numbers),
278+ label: 'Tokens' ,
136279 tooltip: 'Token Count' ,
137280 ),
138281 BottomNavigationBarItem (
139- icon: Icon (
140- Icons .functions,
141- color: Theme .of (context).colorScheme.primary,
142- ),
143- label: 'Function Calling' ,
282+ icon: Icon (Icons .functions),
283+ label: 'Functions' ,
144284 tooltip: 'Function Calling' ,
145285 ),
146286 BottomNavigationBarItem (
147- icon: Icon (
148- Icons .image,
149- color: Theme .of (context).colorScheme.primary,
150- ),
151- label: 'Image Prompt' ,
287+ icon: Icon (Icons .image),
288+ label: 'Image' ,
152289 tooltip: 'Image Prompt' ,
153290 ),
154291 BottomNavigationBarItem (
155- icon: Icon (
156- Icons .image_search,
157- color: Theme .of (context).colorScheme.primary,
158- ),
159- label: 'Imagen Model' ,
292+ icon: Icon (Icons .image_search),
293+ label: 'Imagen' ,
160294 tooltip: 'Imagen Model' ,
161295 ),
162296 BottomNavigationBarItem (
163- icon: Icon (
164- Icons .schema,
165- color: Theme .of (context).colorScheme.primary,
166- ),
167- label: 'Schema Prompt' ,
297+ icon: Icon (Icons .schema),
298+ label: 'Schema' ,
168299 tooltip: 'Schema Prompt' ,
169300 ),
170301 BottomNavigationBarItem (
171- icon: Icon (
172- Icons .edit_document,
173- color: Theme .of (context).colorScheme.primary,
174- ),
175- label: 'Document Prompt' ,
302+ icon: Icon (Icons .edit_document),
303+ label: 'Document' ,
176304 tooltip: 'Document Prompt' ,
177305 ),
178306 BottomNavigationBarItem (
179- icon: Icon (
180- Icons .video_collection,
181- color: Theme .of (context).colorScheme.primary,
182- ),
183- label: 'Video Prompt' ,
307+ icon: Icon (Icons .video_collection),
308+ label: 'Video' ,
184309 tooltip: 'Video Prompt' ,
185310 ),
186311 BottomNavigationBarItem (
187- icon: Icon (
188- Icons .stream,
189- color: Theme .of (context).colorScheme.primary,
190- ),
191- label: 'Bidi Stream' ,
312+ icon: Icon (Icons .stream),
313+ label: 'Bidi' ,
192314 tooltip: 'Bidi Stream' ,
193315 ),
194316 ],
195- currentIndex: _selectedIndex ,
317+ currentIndex: widget.selectedIndex ,
196318 onTap: _onItemTapped,
197319 ),
198320 );
0 commit comments