I am trying to track current Scaffold
s (their BuildContext
s) in order to create an app-wide SnackBar
function. Currently I am creating a class
which presents a Scaffold
and adds its context
to another class
, which manages the currently running Scaffold
s. I did not succeed, however, as my current attempt has two issues:
- It does not properly store the current
Scaffold
s - Apparently the
dispose
method is too late for removing theScaffold
'sBuildContext
from theList
of currentScaffold
s'BuildContext
s, so this presents me with theException
, "Looking up a deactivated widget's ancestor is unsafe.
"
Here is my current attempt:
- The implementation (
main.dart
):
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'MScaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Snackbar manager',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Snackbar manager'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return MScaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child:Icon(Icons.add_circle_outline),
onPressed: (){
MScaffoldManager.showSnackbar();
},
),
FloatingActionButton(
heroTag: 1,
child:Icon(Icons.remove_circle_outline),
onPressed: (){
MScaffoldManager.hideSnackbar();
},
),
FloatingActionButton(
heroTag: 2,
child:Icon(Icons.add),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (context){
return SecondScaffold();
}
)
);
},
),
],
),
);
}
}
class SecondScaffold extends StatelessWidget{
@override
Widget build(BuildContext context){
return MScaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child:Icon(Icons.add_circle_outline),
onPressed: (){
MScaffoldManager.showSnackbar();
},
),
FloatingActionButton(
heroTag: 1,
child:Icon(Icons.remove_circle_outline),
onPressed: (){
MScaffoldManager.hideSnackbar();
},
),
FloatingActionButton(
heroTag: 2,
child:Icon(Icons.remove),
onPressed: (){
Navigator.of(context).pop();
},
),
],
),
);
}
}
- The
library
class
es:MScaffoldManager
;MScaffold
; andMScaffoldState
:
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class MScaffoldManager{
static List<Map> scaffoldInformation = List();
static void addScaffold(context){
scaffoldInformation.add({'context':context});
print("Scaffold added:\n"+scaffoldInformation.toString());
}
static void removeScaffold(context){
Scaffold.of(context).hideCurrentSnackBar();
scaffoldInformation.remove({'context':context});
print("Scaffold removed:\n"+scaffoldInformation.toString());
}
static void showSnackbar(){
scaffoldInformation.forEach((v){
Scaffold.of(v['context']).showSnackBar(SnackBar(
content: Text("Snackbar works"),
));
});
}
static void hideSnackbar(){
scaffoldInformation.forEach((v){
Scaffold.of(v['context']).hideCurrentSnackBar();
});
}
}
class MScaffold extends StatefulWidget{
Key key;
var appBar;
var body;
var floatingActionButton;
var floatingActionButtonLocation;
var floatingActionButtonAnimator;
var persistentFooterButtons;
var drawer;
var endDrawer;
var bottomNavigationBar;
var bottomSheet;
var backgroundColor;
var resizeToAvoidBottomPadding;
var resizeToAvoidBottomInset;
var primary;
var drawerDragStartBehavior;
var extendBody;
var extendBodyBehindAppBar;
var drawerScrimColor;
var drawerEdgeDragWidth;
MScaffold({
Key key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null);
@override
State<StatefulWidget> createState() {
return MScaffoldState(
key: key,
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
persistentFooterButtons: persistentFooterButtons,
drawer: drawer,
endDrawer: endDrawer,
bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
primary: primary,
drawerDragStartBehavior: drawerDragStartBehavior,
extendBody: extendBody,
extendBodyBehindAppBar: extendBodyBehindAppBar,
drawerScrimColor: drawerScrimColor,
drawerEdgeDragWidth: drawerEdgeDragWidth,
);
}
}
class MScaffoldState extends State<MScaffold> {
Key key;
var appBar;
var body;
var floatingActionButton;
var floatingActionButtonLocation;
var floatingActionButtonAnimator;
var persistentFooterButtons;
var drawer;
var endDrawer;
var bottomNavigationBar;
var bottomSheet;
var backgroundColor;
var resizeToAvoidBottomPadding;
var resizeToAvoidBottomInset;
var primary;
var drawerDragStartBehavior;
var extendBody;
var extendBodyBehindAppBar;
var drawerScrimColor;
var drawerEdgeDragWidth;
MScaffoldState({
Key key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null);
@override
void initState() {
super.initState();
}
@override
dispose(){
MScaffoldManager.removeScaffold(_scaffoldContext);
super.dispose();
}
BuildContext _scaffoldContext;
@override
Widget build(BuildContext context) {
return Scaffold(
key: key,
appBar: appBar,
body: Builder(
builder: (context){
if(_scaffoldContext!=null)
MScaffoldManager.removeScaffold(_scaffoldContext);
_scaffoldContext = context;
MScaffoldManager.addScaffold(_scaffoldContext);
return body;
},
),
floatingActionButton: floatingActionButton,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
persistentFooterButtons: persistentFooterButtons,
drawer: drawer,
endDrawer: endDrawer,
bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
primary: primary,
drawerDragStartBehavior: drawerDragStartBehavior,
extendBody: extendBody,
extendBodyBehindAppBar: extendBodyBehindAppBar,
drawerScrimColor: drawerScrimColor,
drawerEdgeDragWidth: drawerEdgeDragWidth,
);
}
}
Acknowledging that it doesn't work as it is, it also seems a little bit verbose and a bit messy. What I would like to do is simply make a Scaffold
class
that functions just like a Scaffold
, but works with a manager class
, which keeps track of all of the Scaffold
s' context
s so I can easily display SnackBar
messages, regardless of what page the user is on.