11import argparse
22import os
3+ from pathlib import Path
34
45from rich .console import Group
56from rich .live import Live
67from rich .panel import Panel
78from rich .table import Column , Table
89
910from flet_cli .commands .build_base import BaseFlutterCommand , console , verbose2_style
11+ from flet_cli .utils .android_sdk import AndroidSDK
1012
1113
1214class Command (BaseFlutterCommand ):
@@ -19,6 +21,7 @@ def __init__(self, parser: argparse.ArgumentParser) -> None:
1921 self .launch_target = None
2022 self .cold_boot = False
2123 self .create_emulator = False
24+ self .delete_emulator = None
2225 self .emulator_name = None
2326
2427 def add_arguments (self , parser : argparse .ArgumentParser ) -> None :
@@ -42,6 +45,12 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
4245 default = False ,
4346 help = "Create a new emulator using Flutter defaults." ,
4447 )
48+ parser .add_argument (
49+ "--delete" ,
50+ dest = "delete" ,
51+ type = str ,
52+ help = "Delete an emulator by ID or name (Android only)." ,
53+ )
4554 parser .add_argument (
4655 "--name" ,
4756 dest = "name" ,
@@ -56,23 +65,28 @@ def handle(self, options: argparse.Namespace) -> None:
5665 self .launch_target = self .options .launch
5766 self .cold_boot = bool (self .options .cold )
5867 self .create_emulator = bool (self .options .create )
68+ self .delete_emulator = self .options .delete
5969 self .emulator_name = self .options .name
6070
61- if self .create_emulator and self .launch_target :
62- console .log (
63- "Please choose either --create or --launch, not both." ,
64- style = verbose2_style ,
65- )
66- self .cleanup (
67- 1 , Panel ("Please choose either --create or --launch, not both." )
68- )
71+ selected_actions = [
72+ bool (self .create_emulator ),
73+ bool (self .launch_target ),
74+ bool (self .delete_emulator ),
75+ ]
76+ if sum (selected_actions ) > 1 :
77+ msg = "Please choose only one action: --create, --launch, or --delete."
78+ console .log (msg , style = verbose2_style )
79+ self .cleanup (1 , msg )
6980
7081 self .status = console .status (
7182 "[bold blue]Initializing environment..." ,
7283 spinner = "bouncingBall" ,
7384 )
7485 with Live (Group (self .status , self .progress ), console = console ) as self .live :
7586 self .initialize_command ()
87+ if self .delete_emulator :
88+ self ._delete_emulator ()
89+ return
7690 if self .create_emulator :
7791 self ._create_emulator ()
7892 return
@@ -120,11 +134,11 @@ def _list_emulators(self):
120134 '\n Run [green]"flet emulators --create"[/green] '
121135 "to create a new emulator.\n "
122136 )
123- self .cleanup (0 , Group (Panel ("No emulators found." ), footer ))
137+ self .cleanup (0 , Group (Panel ("No emulators found." ), footer ), no_border = True )
124138
125139 table = Table (
126- Column ("Name" , style = "cyan" , justify = "left" ),
127140 Column ("ID" , style = "magenta" , justify = "left" , no_wrap = True ),
141+ Column ("Name" , style = "cyan" , justify = "left" ),
128142 Column ("Platform" , style = "green" , justify = "center" ),
129143 Column ("Manufacturer" , style = "yellow" , justify = "left" ),
130144 title = "Available emulators" ,
@@ -134,8 +148,8 @@ def _list_emulators(self):
134148
135149 for emulator in emulators :
136150 table .add_row (
137- emulator ["name" ],
138151 emulator ["id" ],
152+ emulator ["name" ],
139153 emulator ["platform_label" ],
140154 emulator ["manufacturer" ],
141155 )
@@ -146,13 +160,15 @@ def _list_emulators(self):
146160 '[green]"flet emulators --launch <emulator-id>"[/green].\n '
147161 "Create a new emulator with "
148162 '[green]"flet emulators --create [--name <name>]"[/green].\n '
163+ "Delete an Android emulator with "
164+ '[green]"flet emulators --delete <emulator-id>"[/green].\n '
149165 "\n "
150166 "You can find more information on managing emulators at the links below:\n "
151167 " https://developer.android.com/studio/run/managing-avds\n "
152168 " https://developer.android.com/studio/command-line/avdmanager"
153169 )
154170
155- self .cleanup (0 , message = Group (table , footer ))
171+ self .cleanup (0 , message = Group (table , footer ), no_border = True )
156172
157173 def _launch_emulator (self ):
158174 assert self .launch_target
@@ -181,8 +197,10 @@ def _launch_emulator(self):
181197 error_output = launch_result .stderr or output
182198 self .cleanup (
183199 launch_result .returncode ,
184- Panel (
185- error_output or f"Failed to launch emulator '{ self .launch_target } '."
200+ (
201+ error_output
202+ if error_output
203+ else f"Failed to launch emulator '{ self .launch_target } '."
186204 ),
187205 )
188206 return
@@ -191,12 +209,33 @@ def _launch_emulator(self):
191209 console .log (output , style = verbose2_style )
192210
193211 mode = " (cold boot)" if self .cold_boot else ""
194- self .cleanup (
195- 0 ,
196- Panel (
197- f"Emulator [cyan]{ self .launch_target } [/cyan] launched{ mode } ." ,
198- ),
212+ self .cleanup (0 , f"Emulator [cyan]{ self .launch_target } [/cyan] launched{ mode } ." )
213+
214+ def _delete_emulator (self ):
215+ assert self .delete_emulator
216+ self .update_status (
217+ f"[bold blue]Deleting emulator [cyan]{ self .delete_emulator } [/cyan]..."
199218 )
219+ home_dir = self .env .get ("ANDROID_HOME" ) or AndroidSDK .android_home_dir ()
220+ if not home_dir :
221+ self .cleanup (
222+ 1 , "ANDROID_HOME is not set and Android SDK location cannot be found."
223+ )
224+
225+ sdk = AndroidSDK (
226+ self .env .get ("JAVA_HOME" , "" ), self .log_stdout , progress = self .progress
227+ )
228+
229+ try :
230+ sdk .delete_avd (Path (home_dir ), self .delete_emulator )
231+ except Exception as exc : # pragma: no cover - defensive
232+ self .skip_flutter_doctor = True
233+ self .cleanup (
234+ 1 , f"Failed to delete emulator '{ self .delete_emulator } ': { exc } "
235+ )
236+ return
237+
238+ self .cleanup (0 , f"Deleted emulator [cyan]{ self .delete_emulator } [/cyan]." )
200239
201240 def _create_emulator (self ):
202241 self .update_status ("[bold blue]Creating emulator..." )
@@ -220,7 +259,7 @@ def _create_emulator(self):
220259 error_output = create_result .stderr or output
221260 self .cleanup (
222261 create_result .returncode ,
223- Panel ( error_output or "Failed to create emulator." ) ,
262+ error_output or "Failed to create emulator." ,
224263 )
225264 return
226265
@@ -230,11 +269,9 @@ def _create_emulator(self):
230269 created_name = self .emulator_name or "emulator"
231270 self .cleanup (
232271 0 ,
233- Panel (
234- f"Created emulator [cyan]{ created_name } [/cyan]. "
235- "Use `flet emulators` to list it or "
236- f"`flet emulators --launch { created_name } ` to start it." ,
237- ),
272+ f"Created emulator [cyan]{ created_name } [/cyan]. "
273+ "Use `flet emulators` to list it or "
274+ f"`flet emulators --launch { created_name } ` to start it." ,
238275 )
239276
240277 def _parse_emulators_output (self , output : str ) -> list [dict ]:
@@ -247,10 +284,11 @@ def _parse_emulators_output(self, output: str) -> list[dict]:
247284 if len (parts ) < 2 :
248285 continue
249286
250- name = parts [0 ]
251- emulator_id = parts [1 ]
287+ emulator_id = parts [0 ]
288+ name = parts [1 ]
252289 # Skip header rows printed by `flutter emulators` (Id • Name • Platform ...)
253- if name .lower () == "id" and emulator_id .lower () == "name" :
290+ lower_head = {p .lower () for p in parts [:4 ]}
291+ if {"id" , "name" , "platform" }.issubset (lower_head ):
254292 continue
255293 details_segments = parts [2 :]
256294 platform_raw = details_segments [- 1 ] if details_segments else ""
0 commit comments