i cant get subcollection that i created before. i am able to create subcollection named "sinav_gorselleri" after i pressed this RaisedButton and going to SinavOlusturPage with this code:
RaisedButton(
color: Colors.blue,
child: Text("Sınav oluştur"),
onPressed: () async{
final newDoc = await FirebaseFirestore.instance.collection("ders_sinavlari_olusturulanlar")
.add({"baslik": "4oluşturulanSınav2", "gorsel": "gorsel", "konu": "", "ogretmen": "ömer kalfa",
"sira": 3, "tarih": ""});
final idnewDoc = newDoc.id;
debugPrint(idnewDoc);
final newDoc_newCol = await newDoc.collection("sinav_gorselleri")
.add({"gorsel": "https://firebasestorage.googleapis.com/v0/b/sbycpaldemo.appspot.com/o/ders_notlari_gorseller%2Fyeni?alt=media&token=4af59ada-4a8b-45cc-86ef-2f691a5baf62"});
final idnewCol = await newDoc_newCol.id;
debugPrint(idnewCol);
Navigator.of(context,rootNavigator: true).pop('dialog');
Navigator.push(context, MaterialPageRoute(builder: (context)=> SinavOlusturPage(idnewDoc: idnewDoc,)));
}),
and in SinavOlusturPage i am expecting to get first doc in subcollection named "sinav_gorselleri" but cant get it with this code:
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class SinavOlusturPage extends StatefulWidget{
final idnewDoc;
const SinavOlusturPage({Key key, this.idnewDoc}) : super(key: key);
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return SinavOlusturPageState(this.idnewDoc);
}
}
class SinavOlusturPageState extends State {
final idnewDoc;
SinavOlusturPageState(this.idnewDoc);
File _imageSelected;
final _formKey = GlobalKey<FormState>();
final _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(key: _scaffoldKey,
appBar: AppBar(
title: Text("SINAV OLUŞTURMA SAYFASI"),
),
body: ListView(
children: [
Center(
child: Text("..."),
StreamBuilder(
stream: FirebaseFirestore.instance.collection("ders_sinavlari_olusturulanlar/$idnewDoc/sinav_gorselleri").snapshots(),
builder: (context, snapshot){
final querySnapshot = snapshot.data();
return GridView.builder(
itemCount: 3,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 10, crossAxisCount: 2,),
itemBuilder: (context, index){
final mapOlusturulan = querySnapshot.docs[index].data();
final idOlusturulan = querySnapshot.docs[index].id;
return GridTile(
child: Center(
child: Image.network(mapOlusturulan["gorsel"])),
);
});
})
],
),
);
}
}
i did tried
FirebaseFirestore.instance.collection("ders_sinavlari_olusturulanlar").doc(idnewDoc) .collection("sinav_gorselleri").snapshots(), also but cant do it. here is my error that i get all the time:
Performing hot reload...
Syncing files to device SNE LX1...
════════ Exception caught by image resource service ════════════════════════════════════════════════
The following ArgumentError was thrown resolving an image codec:
Invalid argument(s): No host specified in URI file:///gorsel
When the exception was thrown, this was the stack:
#0 _HttpClient._openUrl (dart:_http/http_impl.dart:2407:9)
#1 _HttpClient.getUrl (dart:_http/http_impl.dart:2328:48)
#2 NetworkImage._loadAsync (package:flutter/src/painting/_network_image_io.dart:89:59)
#3 NetworkImage.load (package:flutter/src/painting/_network_image_io.dart:50:14)
#4 ImageProvider.resolveStreamForKey.<anonymous closure> (package:flutter/src/painting/image_provider.dart:504:13)
...
Image provider: NetworkImage("gorsel", scale: 1.0)
Image key: NetworkImage("gorsel", scale: 1.0)
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The method 'call' was called on null.
Receiver: null
Tried calling: call()
The relevant error-causing widget was:
StreamBuilder<QuerySnapshot> file:///C:/ornekler/sby_cpal_demo/lib/Dersler/SinavOlusturPage.dart:39:9
════════════════════════════════════════════════════════════════════════════════════════════════════
Reloaded 22 of 694 libraries in 3.748ms.
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#41144):
Class 'QuerySnapshot' has no instance method 'call'.
Receiver: Instance of 'QuerySnapshot'
Tried calling: call()
The relevant error-causing widget was:
StreamBuilder<QuerySnapshot> file:///C:/ornekler/sby_cpal_demo/lib/Dersler/SinavOlusturPage.dart:39:9
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 SinavOlusturPageState.build.<anonymous closure> (package:sby_cpal_demo/Dersler/SinavOlusturPage.dart:42:50)
#2 StreamBuilder.build (package:flutter/src/widgets/async.dart:525:81)
#3 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:129:48)
#4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by image resource service ════════════════════════════════════════════════
Invalid argument(s): No host specified in URI file:///gorsel
════════════════════════════════════════════════════════════════════════════════════════════════════
"gorsel" is my unique field key of subcollection document. this error realy makes me tired but really need to use subcollections in my app.
i didnt solved this with codings i just removed all the codes, pages and stuffs recorded to firebase firestore and rewrite them all step by step. i guess i get the reason of the error. it was about navigation time. after i pressed the button named Sinav Oluştur i was expecting the creation of the subcollection named "soru_gorselleri" of new document firstly and then navigation to SinavOlusturPage but all of these were happennig reversely so the Page was returning null. after i did all of them step by step with different RisedButtons , all of errors gone and happy end.
Related
I have an app with a listview feature with provider pattern, and it has 5 items of data. Here is my snippet code
ListView.builder(
key: Key("listview_portfolio"),
itemBuilder: (context, index) =>
_itemListPortofolio(data.items[index], context),
itemCount: data.items.length,
)
...
Widget _itemListPortofolio(Portfolio portfolio, BuildContext context) {
return Container(
key: ValueKey("item_list_portfolio"),
margin: EdgeInsets.only(bottom: 16),
child: InkWell(
When I run this code, it doesn't throw an error, but when I do the integration test, it throws an error when trying to click the item listview
'package:flutter_test/src/binding.dart': Failed assertion: line 802 pos 14: '_pendingExceptionDetails != null': A test
overrode FlutterError.onError but either failed to return it to its original state, or had unexpected additional errors that it could
not handle. Typically, this is caused by using expect() before restoring FlutterError.onError.
flutter: dart:core-patch/errors_patch.dart 51:61 _AssertionError._doThrowNew
here's my integration test class
Future<void> tapPortfolioItem({bool scrollUp = false}) async {
await _tester.pumpAndSettle(Duration(seconds: 5));
final Widget itemPortfolio =
find.byKey(ValueKey("item_list_portfolio")).evaluate().last.widget;
await _tester.tap(find.byWidget(itemPortfolio));
}
try the code below:
final Finder instance = find.byKey(ValueKey("item_list_portifolio"));
final _itemListPortofolio test = _tester.widget(instance);
await _tester.tap(instance);
expect(anything, findsOneWidget);
expect(test.color, Colors.red);
see also other way to use pumpAndSeatle() here.
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: postsRef
.document(userId)
.collection("usersPosts")
.document(postId)
.get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
Post post = Post.fromDocument(snapshot.data);
return Center(
child: Scaffold(
appBar: header(context, titleText: post.description),
body: ListView(
children: [
Container(
child: post,
)
],
),
),
);
},
);
}
also this my error
The following NoSuchMethodError was thrown building FutureBuilder<DocumentSnapshot>(dirty, state: _FutureBuilderState<DocumentSnapshot>#37f30):
The method '[]' was called on null.
Receiver: null
Tried calling: []("postId")
The relevant error-causing widget was:
FutureBuilder<DocumentSnapshot> file:///D:/Flutter/KadShare/kadshare/lib/pages/post_screen.dart:18:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 DocumentSnapshot.[] (package:cloud_firestore/src/document_snapshot.dart:29:42)
#2 new Post.fromDocument (package:kadshare/widgets/post.dart:34:18)
#3 PostScreen.build.<anonymous closure> (package:kadshare/pages/post_screen.dart:28:26)
#4 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:773:55)
My Post Class
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc["postId"],
ownerId: doc["ownerId"],
username: doc["username"],
location: doc["location"],
description: doc["description"],
mediaUrl: doc["mediaUrl"],
likes: doc["likes"],
);
}
Your snapshot is a Flutter AsyncSnapshot, specifically a AsyncSnapshot<DocumentSnapshot>. When snapshot.hasData is true, that means the DocumentSnapshot exists.
But a DocumentSnapshot exists even when the underlying document doesn't exist in the database, so you also need to check if the DocumentSnapshot has data, which you do with DocumentSnapshot.exists.
So your check then becomes:
if (!snapshot.hasData && snapshot.data.exists) {
So this change means the spinner will keep being rendered if the document doesn't exist.
Alternatively, you may want to render a different UI if the document doesn't exist:
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
if (!snapshot.data.exists) {
return Text("Document does not exist");
}
Post post = Post.fromDocument(snapshot.data);
return Center(
child: Scaffold(
appBar: header(context, titleText: post.description),
body: ListView(
children: [
Container(
child: post,
)
],
),
),
);
},
Also see What is the difference between existing types of snapshots in Firebase?
Your snapshot.data is null;
Maybe change:
if (!snapshot.hasData || snapshot.data == null) {
i assume you are working on fluttershare course app, in your activity feed page when you press show post function make sure you give userId your currentUser.id passing in a non-null value for the 'postId' parameter as shown here:
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(
userId: currentUser.id,
postId: postId,
),
),
);
}
I know that there are already two posts regarding this question, but I could not solve my issue looking at them. Probably because in my case the issue is different.
The code is the following. I want to load some data from the database while showing a loading page. After the data is loaded, I initialize the provider with the loaded data and then I move into a different page.
This code does not need to be in a StatefulWidget, but I try to put it in a StatefulWidget to solve the issue, but without success.
class _InitDBDataState extends State<_InitDBData> {
#override
Widget build(BuildContext context) {
_fetchData(context);
return const Center(child: const CircularProgressIndicator());
}
Future<void> _fetchData(BuildContext context) async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
}
}
I do not have any error if the application runs from scratch, but when I am doing a "Hot Reload" I get the following error pretty often, and it is annoying since I need to restart the application for each small change in the code.
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#46860)
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#55124)
I/flutter ( 9596): Data fetched
I/flutter ( 9596): context: _InitDBData
E/flutter ( 9596): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9596): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 9596): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 9596): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
package:flutter/…/widgets/framework.dart:3508
E/flutter ( 9596): #1 Element._debugCheckStateIsActiveForAncestorLookup
package:flutter/…/widgets/framework.dart:3522
E/flutter ( 9596): #2 Element.getElementForInheritedWidgetOfExactType
package:flutter/…/widgets/framework.dart:3588
E/flutter ( 9596): #3 Provider.of
package:provider/src/provider.dart:221
E/flutter ( 9596): #4 _InitDBDataState._fetchData
package:productive_diary/initScreen.dart:46
E/flutter ( 9596): <asynchronous suspension>
E/flutter ( 9596): #5 _InitDBDataState.build
I don't know why "fetching data..." is printed twice, and I have no clue on how to solve the issue.
I thought the issue was solved with the solution of Saman Salehi, but working in debug mode I had the same exception in the _fetchData function, that now is called in the function initState()
Exception has occurred.
FlutterError (Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.)
I got another error after applying the edits suggested by Stewie Griffin
.
The error is on the line Provider.of<DataProvider>(context, listen: false).init(initData);
I got it during a hot reload. It seems less common than the other error, so the answer of Stewie Griffin surely improved the stability of my Stewie Griffin
E/flutter (23815): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'owner' was called on null.
E/flutter (23815): Receiver: null
E/flutter (23815): Tried calling: owner
E/flutter (23815): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (23815): #1 Provider.of
package:provider/src/provider.dart:193
E/flutter (23815): #2 _InitDBDataState._fetchData
package:productive_diary/initScreen.dart:49
E/flutter (23815): <asynchronous suspension>
E/flutter (23815): #3 _InitDBDataState.initState
Could you please help me?
First of all, never call async methods inside of build as mentioned. Build method is constantly rebuilt and it will cause your fetch method to be repeated like an infinite loop. After you fix that, you still get the same error because of this part:
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
You shouldn't call Navigator during build. Here is what you need to do:
Add this line to the top of your file to use SchedulerBinding:
import 'package:flutter/scheduler.dart';
Wrap Navigator with SchedulerBinding to wait for completing the state before navigating to another screen. Then, call your async method inside of initState.
class _InitDBDataState extends State<_InitDBData> {
#override
void initState() {
// Call your async method here
_fetchData();
super.initState();
}
Future<void> _fetchData() async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
// Wrap Navigator with SchedulerBinding to wait for rendering state before navigating
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
}
#override
Widget build(BuildContext context) {
return Center(child: CircularProgressIndicator());
}
}
Tip: You don't need to pass the context in a Stateful Widget because you can access it from everywhere.
You shouldn't use the build method for anything other than building UI. build can be called at any time even when it's not on the screen.
I would move the _fetchData to the initState so it wouldn't cause any conflict at the build method.
class _InitDBDataState extends State<_InitDBData> {
#override
void initState() {
super.initState();
_fetchData(context);
}
#override
Widget build(BuildContext context) {
return const Center(child: const CircularProgressIndicator());
}
Future<void> _fetchData(BuildContext context) async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
}
}
Navigator.pushReplacement(context,MaterialPageRoute(builder:(context) => WelcomeScreen()),);
|^| .wrap this code SchedulerBinding.instance!.addPostFrameCallback on above code.
like this below:
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => WelcomeScreen()),
);
});
Schedule a callback for the end of this frame.
Does not request a new frame.
This callback is run during a frame, just after the persistent frame
callbacks (which is when the main rendering pipeline has been
flushed). If a frame is in progress and post-frame callbacks haven't
been executed yet, then the registered callback is still executed
during the frame. Otherwise, the registered callback is executed
during the next frame.
The callbacks are executed in the order in which they have been added.
Post-frame callbacks cannot be unregistered. They are called exactly
once.
Before:
After
SampleCode Here i use rive: ^0.8.1 dartpackage
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:rive/rive.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(home: SimpleAnimation()));
}
setdata(BuildContext context) async {
await Future.delayed(const Duration(seconds: 5), () {
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => WelcomeScreen()),
);
});
});
}
class SimpleAnimation extends StatelessWidget {
const SimpleAnimation({Key? key, this.loading}) : super(key: key);
final bool? loading;
#override
Widget build(BuildContext context) {
setdata(context);
return Scaffold(
body: Center(
child: Container(
height: 200,
width: 200,
child: RiveAnimation.network(
'https://cdn.rive.app/animations/vehicles.riv',
),
),
),
);
}
}
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
child: Text(
"HOME PAGE",
style: TextStyle(fontSize: 50),
),
),
),
);
}
}
if it is caused by accessing ScaffoldMessenger by context then putting it inside a try catch will resolve context error.
try{
ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar();
} catch(e){
print(e);
}
For this probleme you can use one of this two solutions :
first: add scheduler like this :
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
second: add future delayed with some milliseconds like this :
Future.delayed(Duration(microseconds: 200))
.then((value) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
!!! But if you a lot of change of state in the same time this solutions may still give the same error.
if is the case try to use the second solution by augmenting the duration.
I am trying to develop a flutter app. This flutter is creating teams for a card game. After the creation of the team, the points could be counted through the, so that you don't have to think about how many points everybody has.
But I got an exception, where I know where the exception and what it means, but i do not have any clue how i could solve the problem. I hope some of you guys could help me.
This is the code where the error is thrown:
import 'package:flutter/material.dart';
class Punktezaehler extends StatefulWidget{
final List<String> spieler_namen;
Punktezaehler(this.spieler_namen);
#override
State<StatefulWidget> createState() => new _Punktezaehler(this.spieler_namen);
}
class _Punktezaehler extends State<Punktezaehler>{
final List<String> spieler_namen;
_Punktezaehler(this.spieler_namen);
List<int> punkteanzahl_teamEins;
List<int> punkteanzahl_teamZwei;
#override
Widget build(BuildContext context) {
var spieler1 = spieler_namen[0].substring(0,3);
var spieler2 = spieler_namen[1].substring(0,3);
var spieler3 = spieler_namen[2].substring(0,3);
var spieler4 = spieler_namen[3].substring(0,3);
return new Scaffold(
appBar: new AppBar(
automaticallyImplyLeading: false,
title: new Text("$spieler1 & $spieler2 vs" +" $spieler3 & $spieler4"),
actions: <Widget>[
],
),
body: Container(
child: new Row(
children: <Widget>[
new Column(
children: <Widget>[
new IconButton(
icon: Icon(Icons.exposure_plus_2),
onPressed: () => punkte_hinzuzaehlen(1, 2)
)
],
),
new Column(
children: <Widget>[
//new FlatButton(onPressed: () => print(punkteanzahl_teamEins.length), child: new Text("Punkte")),
ListView.builder(
itemCount: punkteanzahl_teamEins.length, //--> Error is thrown here
itemBuilder: (context, index){
return Text(punkteanzahl_teamEins[index].toString());
}
),
new Row()
],
),
new Column(
children: <Widget>[
new IconButton(
icon: Icon(Icons.exposure_plus_2),
onPressed: () => punkte_hinzuzaehlen(2, 2)
)],
)
],
)
),
);
}
void punkte_hinzuzaehlen(int team, int nummer){
if (team == 1){
//Team 1 bekommt die Punkte
print("Team 1 gets points");
}
else if(team == 2){
//Team 2 bekommt die Punkte
print("Team 2 gets points");
}
}
}
And this is the error message:
══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (26028): The following NoSuchMethodError was thrown while handling a gesture:
I/flutter (26028): The getter 'length' was called on null.
I/flutter (26028): Receiver: null
I/flutter (26028): Tried calling: length
After the fix, I got another error:
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (26028): The following assertion was thrown during performResize():
I/flutter (26028): Vertical viewport was given unbounded width.
I/flutter (26028): Viewports expand in the cross axis to fill their container and constrain their children to match
I/flutter (26028): their extent in the cross axis. In this case, a vertical viewport was given an unlimited amount of
I/flutter (26028): horizontal space in which to expand.
punkteanzahl_teamEins is only declared. But not initialized. So it is throwing null error.
You should assign value to punkteanzahl_teamEins as
List<int> punkteanzahl_teamEins = [1,4,5,7];
or pass data from parent as requirement.
We spend lost's of time to resolve issue finally we got a solution is: Please check you pubspec.yaml and remove extra '-' in case of assets file and also please follow the structure of that like spacing and all that.
We are must sure that issue only in pubspec.yaml file
First one check spieler_namen is null or not.If it is null then use below code i hope this will solve your problem.....
if(spieler_namen == null){
new Container(width: 10.0,height: 10.0,);
}else{
your requirement .......
}
In our case, we got this error when Internet Connection is off after calling webservice.
Initialize your list, and run hot restart (press R).
It works correctly.
If you called api in your project. check your device network connection. try to re start your simulator. that will fix this error.
I fix this by revisiting this section in pubspec.yaml
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
Ensure your spacing, hyphens and filenames are correct.
Note: You do not need to list every image file; instead, you can just list their directory:
assets:
- images/
I'm working on a finance app and I'd like a custom textentry field and keyboard for currency entry with an inbuilt calculator.
I've tried using a BottomSheet, both persistent and modal. The modal behaviour is ideal, but it always shows a barrier. The persistent one is what I have now, using a focus node to show and hide it, but it's throwing strange errors:
I/flutter (30319): The following NoSuchMethodError was thrown while dispatching notifications for FocusNode:
I/flutter (30319): The method 'removeLocalHistoryEntry' was called on null.
I/flutter (30319): Receiver: null
I/flutter (30319): Tried calling: removeLocalHistoryEntry(Instance of 'LocalHistoryEntry')
I/flutter (30319):
I/flutter (30319): When the exception was thrown, this was the stack:
I/flutter (30319): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter (30319): #1 LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:296:12)
I/flutter (30319): #2 _NumpadFieldState.initState.<anonymous closure> (file:///D:/code/financepie/lib/widgets/numpad/numpadfield.dart:30:32)
...
In any case, the bottom sheet behaviour (dragging down) isn't really ideal to copy the android/ios soft keyboard. Any better solutions? Current code below:
import 'package:flutter/material.dart';
import 'numpad.dart';
class NumpadField extends StatefulWidget {
#override
_NumpadFieldState createState() {
return new _NumpadFieldState();
}
}
class _NumpadFieldState extends State<NumpadField> {
ValueNotifier<List<String>> state;
FocusNode focusNode;
PersistentBottomSheetController bottomSheetController;
#override initState() {
super.initState();
state = ValueNotifier<List<String>>([]);
state.addListener(() => setState((){}));
focusNode = FocusNode();
focusNode.addListener(() {
print(focusNode);
if (focusNode.hasFocus) {
bottomSheetController = showBottomSheet(
context: context,
builder: (context) => Numpad(state: state),
);
} else {
bottomSheetController?.close(); ///this line causing the error
}
});
}
#override dispose() {
state.dispose();
focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(focusNode);
},
child: Container(
child: Text(state.value.fold<String>("", (str, e) => "$str $e")),
constraints: BoxConstraints.expand(height: 24.0),
decoration: BoxDecoration(
border: BorderDirectional(bottom: BorderSide())
),
),
);
}
}
bottomSheetController?.close(); is nullable. Since the line causes the error, you can add a null-check to prevent this issue.
else {
if(bottomSheetController != null)
bottomSheetController!.close();
}