Why is this bloc getter called on null? - dart

Previously i was initializing my PersonBlocProvider in my main.dart
return PersonBlocProvider(
personBloc: PersonBloc(this._apiInterface),
child: AlarmBlocProvider(
alarmBloc: AlarmBloc(this._apiInterface),
child: EventBlocProvider(
eventBloc: EventBloc(this._apiInterface),
child: MaterialApp(
title: 'Hat Mobile',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: LoginScreen(),
)
)
)
);
And using the BLOCs in another view like this:
class AlarmView extends StatelessWidget {
Widget build(BuildContext context) {
final personBlocProvider = PersonBlocProvider.of(context);
final alarmBlocProvider = AlarmBlocProvider.of(context);
return
DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, right: 10),
child: Text(StringLiterals.ALARM_TEXT)),
Padding(
padding: EdgeInsets.only(right: 15),
child: _BuildSwitch(bloc: alarmBlocProvider))
],
title: Text(StringLiterals.PAGE_TITLE_ALARM),
bottom: MyTabBar(),
),
body: TabBarView(
children: <Widget>[
_ListPage(StringLiterals.PRESENT, personBlocProvider),
_ListPage(StringLiterals.SAFE, personBlocProvider),
_ListPage(StringLiterals.UNKNOWN, personBlocProvider),
_ListPage(StringLiterals.HOLIDAY, personBlocProvider),
],
),
),
);
}
}
This worked, but was spawning BLoCs as soon as app started and I realised i don't want that.
So i decided to move the BlocProviders from main to my view file.
my main became
return MaterialApp(
title: 'Hat Mobile',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: LoginScreen(),
);
and view:
class AlarmView extends StatelessWidget {
Widget build(BuildContext context) {
final personBlocProvider = PersonBlocProvider.of(context);
final alarmBlocProvider = AlarmBlocProvider.of(context);
return PersonBlocProvider(
personBloc: PersonBloc(BlueApiMock()),
child: AlarmBlocProvider(
alarmBloc: AlarmBloc(BlueApiMock()),
child: DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, right: 10),
child: Text(StringLiterals.ALARM_TEXT)),
Padding(
padding: EdgeInsets.only(right: 15),
child: _BuildSwitch(bloc: alarmBlocProvider))
// child: _BuildSwitch(bloc: AlarmBloc(BlueApiMock())))
],
title: Text(StringLiterals.PAGE_TITLE_ALARM),
bottom: MyTabBar(),
),
body: TabBarView(
children: <Widget>[
_ListPage(StringLiterals.PRESENT, personBlocProvider),
_ListPage(StringLiterals.SAFE, personBlocProvider),
_ListPage(StringLiterals.UNKNOWN, personBlocProvider),
_ListPage(StringLiterals.HOLIDAY, personBlocProvider),
],
),
),
)
));
}
}
But now this is resulting in error:
I/flutter ( 6448): The following NoSuchMethodError was thrown building AlarmView(dirty):
I/flutter ( 6448): The getter 'personBloc' was called on null.
I/flutter ( 6448): Receiver: null
I/flutter ( 6448): Tried calling: personBloc
I/flutter ( 6448):
I/flutter ( 6448): When the exception was thrown, this was the stack:
I/flutter ( 6448): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
I/flutter ( 6448): #1 PersonBlocProvider.of (package:red_photon/alarm/providers/person_bloc_provider.dart:14:30)
I/flutter ( 6448): #2 AlarmView.build (package:red_photon/alarm/views/alarm_view.dart:17:51)
Why is it null now?

Yes, you cannot do that as the context you are passing will not have any bloc ancestor though, instead try wrapping DefaultTabController in a Builder Widget and use that context(it will have the bloc as ancestors in widget tree).
Example:
class AlarmView extends StatelessWidget {
Widget build(BuildContext context) {
return PersonBlocProvider(
personBloc: PersonBloc(BlueApiMock()),
child: AlarmBlocProvider(
alarmBloc: AlarmBloc(BlueApiMock()),
child: Builder(
builder: (context) {
final personBlocProvider = PersonBlocProvider.of(context);
final alarmBlocProvider = AlarmBlocProvider.of(context);
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, right: 10),
child: Text(StringLiterals.ALARM_TEXT)),
Padding(
padding: EdgeInsets.only(right: 15),
child: _BuildSwitch(bloc: alarmBlocProvider))
// child: _BuildSwitch(bloc: AlarmBloc(BlueApiMock())))
],
title: Text(StringLiterals.PAGE_TITLE_ALARM),
bottom: MyTabBar(),
),
body: TabBarView(
children: <Widget>[
_ListPage(StringLiterals.PRESENT, personBlocProvider),
_ListPage(StringLiterals.SAFE, personBlocProvider),
_ListPage(StringLiterals.UNKNOWN, personBlocProvider),
_ListPage(StringLiterals.HOLIDAY, personBlocProvider),
],
),
),
);
},
),
),
);
}
}
This should work.
Hope it helps!

Related

Alert dialog didnt invoke

Content of this code is after a QR scan it need alert a dialog with confirm to redirect for that i use ShowDialog in the function it need a context of Widget so I used AlertDialog directly.
If i use ShowDialog in side the scaffold i didnt get the scanned data of Qr code. So what to do?
This is the code:
import 'package:appteq_solutions/dashboard.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:url_launcher/url_launcher.dart';
class Scanner extends StatelessWidget {
Scanner({super.key});
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
late QRViewController controller;
Future<Void> _onQRViewCreated(QRViewController controller) {
this.controller = controller;
controller.scannedDataStream.listen((scanData) async {
controller.pauseCamera();
print(scanData.code);
final uri = Uri.parse(scanData.code as String);
AlertDialog(
title: Text('Redirect To The Url'),
content: SingleChildScrollView(
child: ListView(),
),
actions: <Widget>[
ElevatedButton(
onPressed: () {},
child: Text('No'),
),
ElevatedButton(
child: const Text("Confirm"),
onPressed: () async {
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
controller.resumeCamera();
}
},
),
]);
// }
});
return ;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QR Scanner'),
),
body: Stack(
children: [
Column(
children: <Widget>[
Expanded(
flex: 5,
child: Stack(
children: [
QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
),
Center(`your text`
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 4,
),
borderRadius: BorderRadius.circular(12),
),
),
),
],
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => Dashboard()));
},
child: Center(
child: Text(
' Press Here To Go DashBoard',
style:
TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
),
],
),
],
),
);
}
}
Help me use Alerdialog in function or to pass the data in dcaffold.

In CupertinoNavigationBar how do i show a button besides back button in leading?

I tried something like this:
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(widget.title),
),
child: Center(
child: Container(
child: CupertinoButton.filled(
child: const Text('Push screen'),
onPressed: () {
CupertinoNavigationBar navBar = CupertinoNavigationBar(
leading: Row(children: <Widget>[
const CupertinoNavigationBarBackButton(),
CupertinoButton(
child: const Text('Button 2'),
padding: EdgeInsets.zero,
onPressed: () {},
),
]),
);
Navigator.push(context, CupertinoPageRoute<CupertinoPageScaffold>(
builder: (_) => CupertinoPageScaffold(
navigationBar: navBar,
child: Center(child: const Text('Content')),
)
));
},
),
),
),
);
}
And when tapping the button, it fails with
I/flutter (30855): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (30855): The following NoSuchMethodError was thrown building CupertinoNavigationBarBackButton(dirty):
I/flutter (30855): The getter 'canPop' was called on null.
I/flutter (30855): Receiver: null
I/flutter (30855): Tried calling: canPop
The reason is that this code in CupertinoNavigationBarBackButton returns null
final ModalRoute<dynamic> currentRoute = ModalRoute.of(context);
I wonder why that's the case? Is it because when I push the button, context still hasn't gotten the route yet?
What's the correct way to manually add a back button?
Thanks in advance.
Here is a very simple snippet of code. I hope it helps you:
class DemoView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: GestureDetector(
onTap: () {
debugPrint('Back button tapped');
},
child: Row(
children: <Widget>[
Icon(CupertinoIcons.left_chevron),
Text(
'Back',
style: TextStyle(
color: CupertinoColors.activeBlue,
),
),
],
),
),
middle: Text('Demo'),
trailing: GestureDetector(
onTap: () {
debugPrint('add icon tapped');
},
child: Icon(
CupertinoIcons.add,
color: CupertinoColors.black,
),
),
),
child: Center(
child: Text(
'Content',
style: TextStyle(fontSize: 36.0),
),
),
);
}
}
Which is the correct way to add manually back button?
I use code like this:
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(widget.title),
leading: CupertinoNavigationBarBackButton(
onPressed: () => Navigator.of(context).pop(),
),
child: _myAppBody() // return widget
),
);
}

Flutter - Calling persistent bottom sheet from another class

I have a persistent bottom sheet in Flutter which currently exists inside an icons onPressed(){} property.
I would like to move this persistent bottom sheet to a new class on its own but I can't seem to get it working. I am still new to flutter and can't work out how to build the structure for the persistent bottom bar.
I have currently tried the below but when I run my code, I get an exception that is thrown.
main.dart
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Test App'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.open_in_new),
onPressed: (){
ShowBottomSheet(context);
},
)
],
),
bottom_modal_sheet.dart
final GlobalKey<ScaffoldState> _scaffoldKey = new
GlobalKey<ScaffoldState>();
void ShowBottomSheet(BuildContext context) {
_scaffoldKey.currentState.showBottomSheet<Null>((BuildContext context)
{
return new Container(
width: double.infinity,
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 24.0, top: 16.0),
child: Text('Site Location',
style: TextStyle(fontSize: 24.0, color: Color(0xFF1181A1), fontWeight: FontWeight.bold,),
),
),
Padding(
padding: EdgeInsets.only(left: 24.0, top: 16.0),
child: Text('11 Carr Road, Three Kings, Auckland 1042',
textAlign: TextAlign.left,
style: TextStyle(fontSize: 16.0, color: Color(0xFF8596AC)),
),
),
Padding(
padding: EdgeInsets.only(left: 24.0, top: 24.0, right: 24.0, bottom: 16.0),
child: RasiedGradientButton(
child: Text('Set Location', style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold, color: Colors.white)),
gradient: LinearGradient(
colors: <Color>[Color(0xFFFCCF58), Color(0xFFFEAA00)]
),
onPressed: (){
print('button clicked');
},
)
),
],
));
});
}
I am getting the error:
I/flutter (19024): ══╡ EXCEPTION CAUGHT BY GESTURE
╞═══════════════════════════════════════════════════════════════════
I/flutter (19024): The following NoSuchMethodError was thrown while
handling a gesture:
I/flutter (19024): The method 'showBottomSheet' was called on null.
I/flutter (19024): Receiver: null
I/flutter (19024): Tried calling: showBottomSheet<Null>(Closure:
(BuildContext) => Container)
I/flutter (19024):
You just need call showBottomSheet in your screen widget that you want show the bottom sheet and return the widget of your custom bottom sheet. The snippet show how to do this. Read the source comments.
// our Screen widget class
class MyScreen extends StatefulWidget {
#override
_MyScreenScreenState createState() => _MyScreenScreenState();
}
class _MyScreenState extends State<MyScreenScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test App'),
actions: <Widget>[
IconButton( icon: Icon(Icons.open_in_new),
onPressed: (){
_showMyBottomSheet();
},
)
],
),
);
}
void _showMyBottomSheet(){
// the context of the bottomSheet will be this widget
//the context here is where you want to showthe bottom sheet
showBottomSheet(context: context,
builder: (BuildContext context){
return MyBottomSheetLayout(); // returns your BottomSheet widget
}
);
}
}
//your bottom sheet widget class
//you can put your things here, like buttons, callbacks and layout
class MyBottomSheetLayout extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(); // return your bottomSheetLayout
}
}

How do I remove a child that's been pressed from ListView

I have a ListView in which I will dynamically add in some children of same type. Inside every children widget has a button. What I want to implement is, that, when user presses the button on a child widget, this child widget will be removed from the ListView. I can do this in C# using events, but I'm a total noob to Dart and Flutter.
Here is my ListView
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Edit Plan'),
backgroundColor: Colors.green,
actions: <Widget>[
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
txts.add('set');
});
},
),
)
],
),
backgroundColor: Colors.white,
body: ListView(
children: txts.map((string) =>
new ListViewItem()).toList(growable: false),
),
);
}
And here is my listViewItem:
class ListViewItem extends StatelessWidget {
final Workout workout;
ListViewItem({Key key, #required this.workout})
: assert(workout != null),
super(key: key);
#override
Widget build(BuildContext context) {
// TODO: implement build
final theme = Theme.of(context);
return Padding(
padding: EdgeInsets.all(12),
child: Card(
elevation: 12,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text(
'The Enchanted Nightingale',
style: TextStyle(color: Colors.white),
),
subtitle: Text(
'Music by Julie Gable. Lyrics by Sidney Stein.',
style: TextStyle(color: Colors.white),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Name your workout',
labelStyle: TextStyle(color: Colors.white)),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'DELETE',
style: TextStyle(color: Colors.white),
),
onPressed: () {},
),
],
),
),
],
),
)),
);
}
}
I edited your code to use a ListView.builder, you need to remove the item at index from the List (txts) you are using, your code will be as follows:
List<String> txts = List();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Plan'),
backgroundColor: Colors.green,
actions: <Widget>[
Builder(
builder: (context) =>
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
txts.add('set');
});
},
),
)
],
),
backgroundColor: Colors.white,
body: new ListView.builder(
itemCount: txts.length,
itemBuilder: (BuildContext context, int index) {
return ListViewItem(
workout: workout,
onDelete: (){
setState(() {
txts.removeAt(index);
});
},
);
},
),
);
}
in addition to that you need to add an ondelete callback in the ListViewItem, the code in the ListViewItem class will be as follows:
class ListViewItem extends StatelessWidget {
final Workout workout;
final VoidCallback onDelete;
ListViewItem({Key key, #required this.workout, this.onDelete})
: assert(workout != null),
super(key: key);
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: EdgeInsets.all(12),
child: Card(
elevation: 12,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text(
'The Enchanted Nightingale',
style: TextStyle(color: Colors.white),
),
subtitle: Text(
'Music by Julie Gable. Lyrics by Sidney Stein.',
style: TextStyle(color: Colors.white),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Name your workout',
labelStyle: TextStyle(color: Colors.white)),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'DELETE',
style: TextStyle(color: Colors.white),
),
onPressed: () =>onDelete(),
),
],
),
),
],
),
)),
);
}
}

IconButton throws an exception

I'm trying to make a simple AppBar widget with some icons in Flutter, but I keep getting this assertion:
The following assertion was thrown building IconButton(Icon(IconData(U+0E5D2)); disabled; tooltip:
I pretty much just mimiced the documentation, but here is my code:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
title: "Stateless Widget Example",
home: new AppBar(title: new Text("App Bar"))
));
}
class AppBar extends StatelessWidget {
AppBar({this.title});
final Widget title;
#override
Widget build(BuildContext context) {
return new Container(
height: 56.0,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: new BoxDecoration(
color: Colors.cyan,
border: new Border(
bottom: new BorderSide(
width: 1.0,
color: Colors.black
)
)
),
child: new Row (
children: <Widget> [
new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
new Expanded(child: title)
]
)
);
}
}
I feel like I'm missing an import or something. But I'm not completely sure. Perhaps my computer was just acting up, because Flutter run has been buggy for me. I'm new to Dart and Flutter, so perhaps I'm just not seeing an obvious error.
IconButton is a Material Widget, so you need to use it inside a Material parent, for example something like this:
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text(widget.title), actions: <Widget>[
new IconButton(
icon: new Icon(Icons.favorite),
tooltip: 'Favorite',
onPressed: () {},
),
new IconButton(
icon: new Icon(Icons.more_vert),
tooltip: 'Navigation menu',
onPressed: () {},
),
]),
body: new Center(
child: new Text('This is the body of the page.'),
),
);
}
Check to ensure that your pubspec.yaml contains the following:
flutter:
uses-material-design: true
That will ensure that the Material Icons font is included with your application, so that you can use the icons in the Icons class.
addButton() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: SizedBox(
height: 45,
width: 200,
child: ElevatedButton.icon(
onPressed: () async {},
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
)),
elevation: MaterialStateProperty.all(1),
backgroundColor: MaterialStateProperty.all(Colors.blue),
),
icon: Icon(Icons.add, size: 18),
label: Text("Add question"),
),
),
),
],
);
}
you lost meterial widget
IconButton needs Material
return new Material(
child: new Container(
height: 56.0,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: new BoxDecoration(
color: Colors.cyan,
border: new Border(
bottom: new BorderSide(
width: 1.0,
color: Colors.black
)
)
),
child: new Row (
children: <Widget> [
new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
new Expanded(child: title)
]
)
);

Resources