Skip to content

Instantly share code, notes, and snippets.

@mikaelj
Last active October 7, 2019 16:20
Show Gist options
  • Save mikaelj/ca604bdec6fa8dbdd9d23bf7b9b74647 to your computer and use it in GitHub Desktop.
Save mikaelj/ca604bdec6fa8dbdd9d23bf7b9b74647 to your computer and use it in GitHub Desktop.
return BlocListener<MyBloc, MyState>(
listener: (context, state) {
if (state is QuestionState && state.attempts == 0) {
final attempts = state.attempts;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return BlocBuilder<MyBloc, MyState>(
builder: (context, state) {
final bloc = BlocProvider.of<MyBloc>(context);
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
title: Text(
"($attempts) Title"),
content: Text(
"($attempts) Message"),
actions: <Widget>[
FlatButton(
child: Text("Cantel"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Retry"),
onPressed: () {
// RetryEvent will be mapped to QuestionState in MyBloc
bloc.dispatch(RetryEvent(
attempts: attempts + 1));
},
),
],
)
);
},
);
}
);
}
},
);
@felangel
Copy link

felangel commented Oct 6, 2019

How about something like:

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

abstract class QuestionEvent {}

class Retry extends QuestionEvent {}

class Reset extends QuestionEvent {}

abstract class QuestionState {}

class InitialQuestionState extends QuestionState {}

class QuestionInProgressState extends QuestionState {
  final int attempts;
  QuestionInProgressState(this.attempts);
}

class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
  @override
  QuestionState get initialState => InitialQuestionState();

  @override
  Stream<QuestionState> mapEventToState(
    QuestionEvent event,
  ) async* {
    if (event is Retry) {
      final state = currentState;
      yield state is QuestionInProgressState
          ? QuestionInProgressState(state.attempts + 1)
          : QuestionInProgressState(0);
    } else if (event is Reset) {
      yield InitialQuestionState();
    }
  }
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) => QuestionBloc(),
      child: MaterialApp(
        home: QuestionPage(),
      ),
    );
  }
}

class QuestionPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocListener<QuestionBloc, QuestionState>(
        condition: (_, current) =>
            current is QuestionInProgressState && current.attempts == 0,
        listener: (context, state) {
          showDialog(
            context: context,
            barrierDismissible: false,
            builder: (context) => _QuestionDialog(),
          );
        },
        child: Center(child: Text('Question Page')),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.help),
        onPressed: () {
          BlocProvider.of<QuestionBloc>(context).dispatch(Retry());
        },
      ),
    );
  }
}

class _QuestionDialog extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<QuestionBloc, QuestionState>(
      builder: (context, state) {
        final attempts = state is QuestionInProgressState ? state.attempts : 0;
        return WillPopScope(
          onWillPop: () async => false,
          child: AlertDialog(
            title: Text('($attempts) Title'),
            content: Text('($attempts) Message'),
            actions: <Widget>[
              FlatButton(
                child: Text('Cancel'),
                onPressed: () {
                  Navigator.of(context).pop();
                  BlocProvider.of<QuestionBloc>(context).dispatch(Reset());
                },
              ),
              FlatButton(
                child: Text('Retry'),
                onPressed: () {
                  BlocProvider.of<QuestionBloc>(context).dispatch(Retry());
                },
              ),
            ],
          ),
        );
      },
    );
  }
}

@mikaelj
Copy link
Author

mikaelj commented Oct 6, 2019

@felangel

Thanks! I'll give it a try. I find the showDialog <-> Flutter/Bloc interaction particularly hairy. Happy you're always so helpful. :-)
Maybe something for a recipe?

@felangel
Copy link

felangel commented Oct 6, 2019

No problem! @mikaelj let me know if it addresses your question and I'm happy to add a recipe for blocs and dialogs 👍

@mikaelj
Copy link
Author

mikaelj commented Oct 7, 2019

@felangel Works great and turns out wasn't far away from what I had - I think moving things out to widgets (like you did) did the trick. Makes (seemingly) complex code more readable. Yeah, be great to have that as a recipe for others!

Then once the user has the basics (with your snippet), they can easily do more complex stuff like using a FutureBuilder for changing text/actions after a delay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment