Inheriting StatefulWidgets causing scaffold context error

37 views
Skip to first unread message

Bob Marley

unread,
Jun 18, 2020, 10:33:39 AM6/18/20
to Flutter Development (flutter-dev)
I'm trying to create widgets to build views in a way I dont have to type the same code every time. This is what I came up with:


/// Common class for all views
class GerenciadorTelaLogada<C extends StatefulWidget> extends State<C> {
  /// View title
  final String titulo;

  // Child widgets
  final Widget child;

  GerenciadorTelaLogada(
      {@required this.titulo, this.child, this.floatingActButton});

  final Widget floatingActButton;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: Text(this.titulo),
        centerTitle: true,
      ),

      // Side Menu
      drawer: GMenu(),

      floatingActionButton: this.floatingActButton,

      // View body
      body: SafeArea(
        child: GestureDetector(
          onTap: () {
            FocusScope.of(context).requestFocus(new FocusNode());
          },
          child: Container(
            color: Color(0xff5b71b3),
            padding: EdgeInsets.only(top: 10),
            child: this.child,
          ),
        ),
      ),
    );
  }
}

Then, I created another class that adds some functionality to GerenciadorTelaLogada class:


// Standard class to all views that as a Form
class GerenciadorTelaLogadaCadastro<C extends StatefulWidget>
    extends GerenciadorTelaLogada<C> {

  /// Executed when clicked in save button
  final VoidCallback onSalvar;

  /// Construtor
  GerenciadorTelaLogadaCadastro(
      {@required String titulo, Widget child, @required this.onSalvar})
      : super(titulo: titulo, child: child);

// Adds new widgets
  @override
  Widget build(BuildContext context) {
    FloatingActionButton salvarFloatButton = FloatingActionButton(
      child: Icon(
        Icons.save,
        color: Colors.white,
      ),
      backgroundColor: Colors.green,
      onPressed: this.onSalvar,
    );

/// Return the base view with additional widgets
    return GerenciadorTelaLogada(
      titulo: this.titulo,
      child: this.child,
      floatingActButton: salvarFloatButton,
    ).build(context);
  }
}

Now I can create several views using that class, like so:

class CBandeira extends StatefulWidget {
      @override
      _CBandeiraState createState() => _CBandeiraState();
    }



class _CBandeiraState extends GerenciadorTelaLogadaCadastro<CBandeira>
    with SingleTickerProviderStateMixin {
  final _formKey = GlobalKey<FormState>();

var formInputs = <GTextBox>[];

@override
  Widget build(BuildContext context) {
    return GerenciadorTelaLogadaCadastro(
      onSalvar: () {
        if (_formKey.currentState.validate()) {
          Scaffold.of(context).showSnackBar(new SnackBar(
            content: Text("Test"),
          ));
        }
      },
      titulo: "Cadastro de Bandeira",
      child: Form(
        key: _formKey,
        child: ListView.separated(
            itemBuilder: (context, index) {
              return ListTile(
                leading: Icon(
                  formInputs[index].icon,
                  color: Colors.white,
                ),
                title: formInputs[index],
              );
            },
            separatorBuilder: (context, index) {
              return SizedBox(height: 10);
            },
            itemCount: formInputs.length),
      ),
    ).build(context);
  }
}

Everything works fine except when I try to show a Snackbar, _CBandeiraState, onSalvar function return this error:


I/flutter (20636): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (20636): The following assertion was thrown while handling a gesture:
I/flutter (20636): Scaffold.of() called with a context that does not contain a Scaffold.
I/flutter (20636): No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This
I/flutter (20636): usually happens when the context provided is from the same StatefulWidget as that whose build
I/flutter (20636): function actually creates the Scaffold widget being sought.
I/flutter (20636): There are several ways to avoid this problem. The simplest is to use a Builder to get a context that
I/flutter (20636): is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
I/flutter (20636):   https://api.flutter.dev/flutter/material/Scaffold/of.html
I/flutter (20636): A more efficient solution is to split your build function into several widgets. This introduces a
I/flutter (20636): new context from which you can obtain the Scaffold. In this solution, you would have an outer widget
I/flutter (20636): that creates the Scaffold populated by instances of your new inner widgets, and then in these inner
I/flutter (20636): widgets you would use Scaffold.of().
I/flutter (20636): A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the
I/flutter (20636): key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
I/flutter (20636): The context used was:
I/flutter (20636):   CBandeira
I/flutter (20636):
I/flutter (20636): When the exception was thrown, this was the stack:
I/flutter (20636): #0      Scaffold.of (package:flutter/src/material/scaffold.dart:1456:5)
I/flutter (20636): #1      _CBandeiraState.build.<anonymous closure> (package:gerenciador/views/core/cartao/c_bandeira.dart:43:20)
I/flutter (20636): #2      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:779:19)
I/flutter (20636): #3      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:862:36)
I/flutter (20636): #4      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
I/flutter (20636): #5      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:504:11)
I/flutter (20636): #6      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:282:5)
I/flutter (20636): #7      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:217:7)
I/flutter (20636): #8      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9)
I/flutter (20636): #9      PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:76:12)
I/flutter (20636): #10     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:122:9)
I/flutter (20636): #11     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)
I/flutter (20636): #12     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:120:18)
I/flutter (20636): #13     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:106:7)
I/flutter (20636): #14     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:218:19)
I/flutter (20636): #15     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22)
I/flutter (20636): #16     GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
I/flutter (20636): #17     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)
I/flutter (20636): #18     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)
I/flutter (20636): #22     _invoke1 (dart:ui/hooks.dart:275:10)
I/flutter (20636): #23     _dispatchPointerDataPacket (dart:ui/hooks.dart:184:5)
I/flutter (20636): (elided 3 frames from dart:async)
I/flutter (20636):
I/flutter (20636): Handler: "onTap"
I/flutter (20636): Recognizer:
I/flutter (20636):   TapGestureRecognizer#033d5
I/flutter (20636): ════════════════════════════════════════════════════════════════════════════════════════════════════
Am I missing something to accomplish what I am trying to do? I started flutter just a few days ago so I wouldn't be surprised if I am doing this the wrong way.
Reply all
Reply to author
Forward
0 new messages