flutter_firebase_agent_testing 1.0.0
flutter_firebase_agent_testing: ^1.0.0 copied to clipboard
Scaffold Firebase App Testing Agent CI for Flutter Android apps — YAML tests, GitHub Actions, and a setup CLI.
example/lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const FirebaseAgentExampleApp());
}
class FirebaseAgentExampleApp extends StatelessWidget {
const FirebaseAgentExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Agent Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
useMaterial3: true,
),
routes: {
'/': (_) => const HomeScreen(),
'/counter': (_) => const CounterScreen(),
'/signup': (_) => const SignUpScreen(),
},
initialRoute: '/',
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Agent Demo Home')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
Text(
'Welcome to the Agent Demo',
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
const Text(
'Pick a flow below to test.',
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
FilledButton.icon(
key: const Key('btn_open_counter'),
icon: const Icon(Icons.add_circle_outline),
label: const Text('Open Counter'),
onPressed: () => Navigator.of(context).pushNamed('/counter'),
),
const SizedBox(height: 16),
FilledButton.tonalIcon(
key: const Key('btn_open_signup'),
icon: const Icon(Icons.person_add_alt),
label: const Text('Sign Up'),
onPressed: () => Navigator.of(context).pushNamed('/signup'),
),
const Spacer(),
const Center(
child: Text(
'Firebase App Testing Agent demo',
style: TextStyle(color: Colors.grey),
),
),
const SizedBox(height: 16),
],
),
),
);
}
}
class CounterScreen extends StatefulWidget {
const CounterScreen({super.key});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
Center(
child: Text(
'Counter: $_count',
key: const Key('counter_text'),
style: Theme.of(context).textTheme.displaySmall,
),
),
const SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FilledButton(
key: const Key('btn_decrement'),
onPressed: () => setState(() => _count--),
child: const Text('Decrement'),
),
FilledButton(
key: const Key('btn_increment'),
onPressed: () => setState(() => _count++),
child: const Text('Increment'),
),
],
),
const SizedBox(height: 16),
OutlinedButton(
key: const Key('btn_reset'),
onPressed: () => setState(() => _count = 0),
child: const Text('Reset'),
),
const Spacer(),
],
),
),
);
}
}
class SignUpScreen extends StatefulWidget {
const SignUpScreen({super.key});
@override
State<SignUpScreen> createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
String? _submittedName;
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
super.dispose();
}
void _submit() {
if (_formKey.currentState!.validate()) {
setState(() => _submittedName = _nameController.text.trim());
}
}
@override
Widget build(BuildContext context) {
if (_submittedName != null) {
return Scaffold(
appBar: AppBar(title: const Text('Sign Up')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.check_circle, color: Colors.green, size: 72),
const SizedBox(height: 16),
Text(
'Welcome, $_submittedName!',
key: const Key('welcome_text'),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
const Text('Your account has been created.'),
],
),
),
),
);
}
return Scaffold(
appBar: AppBar(title: const Text('Sign Up')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
TextFormField(
key: const Key('field_name'),
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(),
),
validator: (v) => (v == null || v.trim().isEmpty)
? 'Please enter your name'
: null,
),
const SizedBox(height: 16),
TextFormField(
key: const Key('field_email'),
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (v) {
if (v == null || v.trim().isEmpty) {
return 'Please enter your email';
}
if (!v.contains('@')) {
return 'Email must contain @';
}
return null;
},
),
const SizedBox(height: 24),
FilledButton(
key: const Key('btn_submit'),
onPressed: _submit,
child: const Text('Submit'),
),
],
),
),
),
);
}
}