Get familiar with the sample app code

Let's take a look at the code.

  • DemoApp The entrypoint widget for the app
  • LoginPage displays email field and a login button
  • CounterPage displays greeting and a counter, along with a FAB to increment the counter

👉 demo_app.dart

First, the entrypoint widget for the starter app is just a MaterialApp with its home set as LoginPage.

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    // TODO: Wrap with FeatureScope widget.
    return MaterialApp(
      title: 'Feature Flags Demo',
      theme: ThemeData(useMaterial3: true),
      home: const LoginPage(),
    );
  }
}

👉 login_page.dart

Now, let's see the login page. The page has two input options, one is to login using the email address added to the input field and another is to just skip the login. Either way, you'll be navigated to the counter page.

The page will be used to demonstrate the use of Feature Flag without any definite value or variant attached to it. To be specific, we'll change the page title based on feature flag's state.

class LoginPage extends StatefulWidget {
  const LoginPage({super.key});

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final GlobalKey<FormState> _formKey = GlobalKey();
  String? _email;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // TODO: Add title that's shown based on resolved feature flag value.
      appBar: AppBar(
        title: const Text(
          'Feature Widget',
          textAlign: TextAlign.center,
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Text(
                'Login with your email',
                style: Theme.of(context).textTheme.headline6,
              ),
              TextFormField(
                onChanged: (value) => _email = value,
              ),
              ElevatedButton(
                onPressed: () => _navigateToHome(context, email: _email),
                child: const Text('LOGIN'),
              ),
              TextButton(
                onPressed: () => _navigateToHome(context),
                child: const Text('SKIP'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _navigateToHome(BuildContext context, {String? email}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => CounterPage(email: email),
      ),
    );
  }
}

👉 counter_page.dart

Lastly, to see an example using Feature Flag variants and using them on non-UI code, open up this page. You can see two TODOs to implement the Feature Flagging here.

class CounterPage extends StatefulWidget {
  const CounterPage({super.key, required this.email});

  final String? email;

  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int _counter = 0;

  void _incrementCounter() {
    // TODO: Update the increment logic based on feature flag evaluation.
    _counter += 1;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Counter'),
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text.rich(
                TextSpan(
                  text: 'Welcome ',
                  children: [
                    TextSpan(
                      text: widget.email ?? 'anonymous',
                      style: Theme.of(context).textTheme.subtitle1,
                    ),
                    const TextSpan(text: ','),
                  ],
                ),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 16),
              const Text(
                'You have pushed the button this many times:',
                textAlign: TextAlign.center,
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.displayMedium,
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 24),
              // TODO: Wrap the button with FeatureBuilder to change the color based of feature flag variant.
              ElevatedButton(
                onPressed: () => Navigator.pop(context),
                style: ElevatedButton.styleFrom(onPrimary: Colors.deepPurple),
                child: const Text('LOG OUT'),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}