44
55import 'package:firebase_core/firebase_core.dart' ;
66import 'package:flutter/material.dart' ;
7- import 'package:flutter/services.dart' ;
8- import 'package:flutter_current_results/firebase_options.dart' ;
9- import 'package:flutter_current_results/src/routing.dart' ;
10- import 'package:flutter_current_results/src/widgets/app_bar_actions.dart' ;
11- import 'package:go_router/go_router.dart' ;
127import 'package:provider/provider.dart' ;
13- import 'package:url_launcher/url_launcher.dart' as url_launcher;
148
15- import 'filter.dart' ;
16- import 'query.dart' ;
17- import 'results.dart' ;
9+ import 'firebase_options.dart' ;
1810import 'src/auth_service.dart' ;
1911import 'src/platform_specific/url_strategy_stub.dart'
2012 if (dart.library.js_interop) 'src/platform_specific/url_strategy_web.dart' ;
13+ import 'src/routing.dart' ;
2114
2215Future <void > main () async {
2316 WidgetsFlutterBinding .ensureInitialized ();
@@ -66,7 +59,8 @@ class CurrentResultsApp extends StatelessWidget {
6659
6760 @override
6861 Widget build (BuildContext context) {
69- return AppProviders (
62+ return ChangeNotifierProvider <AuthService >(
63+ create: (_) => AuthService (),
7064 child: MaterialApp .router (
7165 title: 'Current Results' ,
7266 theme: ThemeData (
@@ -78,203 +72,3 @@ class CurrentResultsApp extends StatelessWidget {
7872 );
7973 }
8074}
81-
82- class AppProviders extends StatelessWidget {
83- final Widget child;
84- const AppProviders ({super .key, required this .child});
85-
86- @override
87- Widget build (BuildContext context) {
88- return MultiProvider (
89- providers: [
90- ChangeNotifierProvider <AuthService >(create: (_) => AuthService ()),
91- ChangeNotifierProvider <QueryResultsBase >(
92- create: (_) => QueryResults (Filter ('' )),
93- ),
94- ],
95- child: child,
96- );
97- }
98- }
99-
100- class CurrentResultsScreen extends StatefulWidget {
101- final Filter filter;
102- final int initialTabIndex;
103- const CurrentResultsScreen ({
104- super .key,
105- required this .filter,
106- this .initialTabIndex = 0 ,
107- });
108-
109- @override
110- State <CurrentResultsScreen > createState () => _CurrentResultsScreenState ();
111- }
112-
113- class _CurrentResultsScreenState extends State <CurrentResultsScreen >
114- with TickerProviderStateMixin {
115- late final TabController _tabController;
116-
117- @override
118- void initState () {
119- super .initState ();
120- Provider .of <QueryResultsBase >(context, listen: false ).filter =
121- widget.filter;
122- _tabController = TabController (
123- initialIndex: widget.initialTabIndex,
124- length: 3 ,
125- vsync: this ,
126- );
127- }
128-
129- @override
130- void didUpdateWidget (CurrentResultsScreen oldWidget) {
131- super .didUpdateWidget (oldWidget);
132- if (widget.filter != oldWidget.filter) {
133- Provider .of <QueryResultsBase >(context, listen: false ).filter =
134- widget.filter;
135- }
136- _tabController.index = widget.initialTabIndex;
137- }
138-
139- @override
140- Widget build (BuildContext context) {
141- return ChangeNotifierProvider <TabController >.value (
142- value: _tabController,
143- child: CurrentResultsScaffold (tabController: _tabController),
144- );
145- }
146- }
147-
148- class CurrentResultsScaffold extends StatelessWidget {
149- final TabController tabController;
150- const CurrentResultsScaffold ({super .key, required this .tabController});
151-
152- @override
153- Widget build (BuildContext context) {
154- return Align (
155- alignment: Alignment .topLeft,
156- child: Scaffold (
157- appBar: AppBar (
158- centerTitle: true ,
159- leading: const Center (child: FetchingProgress ()),
160- title: const Text (
161- 'Current Results' ,
162- style: TextStyle (fontSize: 24.0 ),
163- ),
164- actions: buildAppBarActions (context),
165- bottom: TabBar (
166- controller: tabController,
167- tabs: const [
168- Tab (text: 'FAILURES' ),
169- Tab (text: 'FLAKES' ),
170- Tab (text: 'ALL' ),
171- ],
172- onTap: (int tab) {
173- final uri = GoRouter .of (
174- context,
175- ).routeInformationProvider.value.uri;
176- final newUri = uri.replace (
177- queryParameters: {
178- for (final entry in uri.queryParameters.entries)
179- if (entry.key != 'showAll' && entry.key != 'flaky' )
180- entry.key: entry.value,
181- if (tab == 2 ) 'showAll' : 'true' ,
182- if (tab == 1 ) 'flaky' : 'true' ,
183- },
184- );
185- GoRouter .of (context).go (newUri.toString ());
186- },
187- ),
188- ),
189- persistentFooterButtons: const [
190- ResultsSummary (),
191- TestSummary (),
192- ApiPortalLink (),
193- JsonLink (),
194- TextPopup (),
195- ],
196- body: const SelectionArea (
197- child: Column (
198- children: [
199- Padding (
200- padding: EdgeInsets .symmetric (horizontal: 24.0 ),
201- child: FilterUI (),
202- ),
203- Divider (color: Colors .black12, height: 20 ),
204- Expanded (child: ResultsPanel ()),
205- ],
206- ),
207- ),
208- ),
209- );
210- }
211- }
212-
213- class ApiPortalLink extends StatelessWidget {
214- const ApiPortalLink ({super .key});
215-
216- @override
217- Widget build (BuildContext context) {
218- return TextButton (
219- child: const Text ('API portal' ),
220- onPressed: () => url_launcher.launchUrl (
221- Uri .https (
222- 'endpointsportal.dart-ci.cloud.goog' ,
223- '/docs/current-results-qvyo5rktwa-uc.a.run.app/g'
224- '/routes/v1/results/get' ,
225- ),
226- ),
227- );
228- }
229- }
230-
231- class JsonLink extends StatelessWidget {
232- const JsonLink ({super .key});
233-
234- @override
235- Widget build (BuildContext context) {
236- return Consumer <QueryResultsBase >(
237- builder: (context, results, child) {
238- return TextButton (
239- child: const Text ('JSON' ),
240- onPressed: () => url_launcher.launchUrl (
241- Uri .https (apiHost, 'v1/results' , {
242- 'filter' : results.filter.terms.join (',' ),
243- 'pageSize' : '4000' ,
244- }),
245- ),
246- );
247- },
248- );
249- }
250- }
251-
252- class TextPopup extends StatelessWidget {
253- const TextPopup ({super .key});
254-
255- @override
256- Widget build (BuildContext context) {
257- return Consumer <QueryResultsBase >(
258- builder: (context, QueryResultsBase results, child) {
259- return Tooltip (
260- message: 'Results query as text' ,
261- waitDuration: const Duration (milliseconds: 500 ),
262- child: TextButton (
263- child: const Text ('Copy to clipboard as text' ),
264- onPressed: () {
265- final text = [resultTextHeader]
266- .followedBy (
267- results.names
268- .expand ((name) => results.grouped[name]! .values)
269- .expand ((list) => list)
270- .map (resultAsCommaSeparated),
271- )
272- .join ('\n ' );
273- Clipboard .setData (ClipboardData (text: text));
274- },
275- ),
276- );
277- },
278- );
279- }
280- }
0 commit comments