Flutter QR how to pass QR data to the next screen - flutter

How do I make it so when the user scan a QR code, the result will then be passed to the next screen.
Here is my code so far,
Widget build(BuildContext context) => SafeArea(
child: Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
buildQrView(context),
Positioned(top: 10, child: buildControlButtons()),
Positioned(bottom: 30, child: buildResult()),
],
),
),
The buildResult is this
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
),
Then the function _dataFetch is as below
_dataFetch() async {
if (barcode == null) {
print('error');
} else {
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
)
);
Navigator.of(context).push(route);
}
I have another class for PassdataQR but its pretty self explanatory. With this code everytime I run it will give me an error
The following _TypeError was thrown building QRScanPage(dirty, dependencies: [MediaQuery], state: _QRScanPageState#720ae):
type 'Future' is not a subtype of type 'String'
and the Navigator functions will be messed up.
Is there another approach I can do, so after a QR code is scanned, the result will be passed to the next screen without errors?

It seems to me that your _dataFetch method returns a futureand in your buildResult method you're using it like so:
Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
)
You can use a futurebuilder to retrieve the async data:
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: FutureBuilder<string>(
future: _dataFetch,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.HasData) {
return Text(snapshot.data, maxLines: 3);
} else return Text('Scan a code!', maxLines: 3);
},
),
);

According to your repository you could just modify line 150:
controller.scannedDataStream
.listen((barcode) => {
setState(() => this.barcode = barcode));
Get.to(new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
));
}
Notice that in order for this to work you'll have to use the Get Package Route Management to navigate to another page. That's because you don't have access to the build context in this code snipped. Normally you would call Navigator.of(context).push(...) but that's not possible without a build context.

Related

The return type 'HomePage' isn't a 'Future, as required by the closure's context

I try to use the class that I made in another file as a class. but it occures error that 'The return type 'HomePage' isn't a 'Future', as required by the closure's context.'
I used to use the class as a function shape like this when similar situation that I show picture below 'stream builder function'. I don't understand to compare with this(In stream builder, return HomePage is okay.) that why in 'if~ replacesnackBar', HomePage() is not valid.
Could you explain what is the problem in this case? I'll really appreciate about that.. Thanks.
--code below--
Case1: In stream builder function.
return SizedBox.expand(
child: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot){
if(!snapshot.hasData){
return SignInScreen(providerConfigs: [
EmailProviderConfiguration()
]);
}
return HomePage();
Case2: In 'if~ replacesnackBar' that I said above.
if (pattern != null) ...[
SizedBox(height: 16),
MaterialButton(
color: Colors.green,
child:
Text("Check Pattern", style: TextStyle(color: Colors.white)),
onPressed: () async {
final result = await Navigator.pushNamed(
context,
"/check_pattern",
arguments: pattern,
);
if (result == true) {
context.replaceSnackbar(
content: Text(
"it's correct",
style: TextStyle(color: Colors.green),
),
);
return HomePage();

Flutter - How to get the value of a provider call function that requires 'await' within a variable?

I'm trying to make a budget app where each budget has its own spending history. Each of those spending histories would have a variable called 'budgetName' which I can compile and total the amount of spending by using sqflite code as below.
return await db.rawQuery("select sum(budgetSpent) as total from spending where budgetName ='" + budgetTitle + "'");
and this works if I try to use a .then((value) {print(value);}) when calling the sqflite function and see the value of each budget's spendings in the debug console.
But the problem is that I need the 'budgetTitle' when calling the function so it can compare with the spending's 'budgetName' to get the total spending amount.
So what I have right now is I try to get the spending amount like below:
child: BudgetCard(
budgetName: budget.budgetName,
budgetSpent: '${Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName}',
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5),
),
But it only returns Instance of 'Future<dynamic>' because it needs the 'await' before getting the value. But I couldn't find another way of doing this because it needs the 'budgetTitle' to be passed on.
Any help, ideas, or suggestions are highly appreciated! thank you in advance.
Here is the database code:
String? budgetSpendingAmount;
getSpecificSpending(budgetTitle) async {
dynamic result =
await SpendingDatabaseHelper.instance.getSpendingAmount(budgetTitle);
String a = result.toString();
debugPrint('A: $a');
if (a == '[{total: null}]') {
a = currency.format(int.parse('000'.trim()));
budgetSpendingAmount = a;
print(budgetSpendingAmount);
} else {
String? b = a.replaceAll(RegExp(r'[{\}\[\]\-]+'), '');
String c = b.substring(b.indexOf(":") + 1);
budgetSpendingAmount = currency.format(int.parse(c.trim()));
}
notifyListeners();
}
Future getSpendingAmount(String budgetTitle) async {
Database db = await instance.database;
return await db.rawQuery("select sum(budgetSpent) as total from spending where ='" + budgetTitle + "'");
}
Here is the full code of where I call the function to get the spending amount data:
Widget build(BuildContext context) {
return FutureBuilder<List<Budget>>(
future: Provider.of<BudgetDatabaseHelper>(context).getBudgets(),
/// Displaying the data from the list
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center();
}
return snapshot.data!.isEmpty
? const Flexible(
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: 80.0),
child: Text(
'You don\'t have any budget',
style: kCaption,
),
)))
: Flexible(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final budget = snapshot.data![index];
return Dismissible(
key: UniqueKey(),
background: const Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.only(bottom: 12.0, right: 24),
child: Icon(
IconlyLight.delete,
color: cRed,
size: 24,
),
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
snapshot.data!.removeAt(index);
Provider.of<BudgetDatabaseHelper>(context,
listen: false)
.removeMethod(budget.id!, budget.budgetName);
},
child: GestureDetector(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
enableDrag: true,
isScrollControlled: true,
builder: (context) {
return DraggableScrollableSheet(
snap: true,
minChildSize: 0.43,
maxChildSize: 0.85,
initialChildSize: 0.43,
snapSizes: const [0.43, 0.85],
builder: (context, scrollController) {
return ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32)),
child: Container(
color: cWhite,
child: SingleChildScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(),
child: BudgetDetails(
id: budget.id!,
budgetName: budget.budgetName,
budgetSpent: 'budgetSpent',
colorValue:
colorSwatch[budget.colorValue],
maxBudget: currency.format(
int.parse(budget.maxBudget)),
svgIcon: iconListBudgetDetails[
budget.iconValue],
),
),
),
);
},
);
},
),
child: BudgetCard(
budgetName: budget.budgetName,
budgetSpent: '${Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName}',
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5),
),
);
},
),
);
},
);
}
Use provider in a widget tree is not a good idea. Make a statefullWidget
Make a getter in your SpendingDatabaseHelper like this
String? _budgetSpendingAmount;
String get budgetSpendingAmount=> _budgetSpendingAmount;
and initialize it like this _budgetSpendingAmount = currency.format(int.parse(c.trim()));
So using this getter you can access this value anywhere in widget tree
Future<void> _getSpecificSpending(String budgetName)async{
try{
await Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budgetName);
} catch(e){
print('error :$e');
}
}
and in your widget tree write something like this
child: FutureBuilder(
future : _getSpecificSpending(budget.budgetName)
builder: (ctx,snapshot){
var spendDataProv=Provider.of<SpendingDatabaseHelper>(context, listen: false);
return snapshot.connectionState==ConnectionState.waiting ?
CircularProgressIndicator() :
BudgetCard(
budgetName: budget.budgetName,
budgetSpent:spendDataProv.budgetSpendingAmount ,
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5)
},
)
Some idea's
Use a FutureBuilder inside your BudgetCard widget. You can then show a CircularProgressIndicator where the spent amount is going to be when you are still waiting on the future to finish.
Or
Use a Boolean flag (which you flip at the beginning of the future method and at the end) that indicates whether the future is finished. Flag false: show progressIndicator, flag true show the spent amount.
Or
When calling Provider.of<BudgetDatabaseHelper>(context).getBudgets() you can let the method getBudgets() also fill an array with the information you need later on. So, call Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName) inside the getBudgets() method for each budgetName you have.

Flutter FutureBuilder Snapshot is null but Future Does return data

While working with Flutter for a new application client for Kanboard, I encountered the following problem. I have a FutureBuilder that should return a select dropdown menu with items but, for some reason, the Snapshot data is null, although the Future method does resolves and has data on return.
Full page.dart code here: https://pastebin.com/J48nxsdZ
The block having the problem is the following:
Widget _columnSelect() {
return FutureBuilder(
future: columnProvider.getColumns(task.projectId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<DropdownMenuItem<String>> columnList = [];
if (snapshot.hasData) {
columnList.add(DropdownMenuItem<String>(
child: Text('Select Column'), value: 0.toString()));
_columns = snapshot.data;
} else {
columnList.add(DropdownMenuItem<String>(
child: Text('Loading..'), value: 0.toString()));
}
_columns.forEach((column) {
columnList.add(DropdownMenuItem<String>(
child: Container(
child: Text(
column.title,
),
),
value: column.id.toString()));
});
return Container(
// margin: EdgeInsets.only(left: 40.0),
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DropdownButtonFormField(
icon: Padding(
padding: const EdgeInsets.only(right: 12),
child: Icon(Icons.view_column, color: Colors.blue),
),
items: columnList,
value: _columnId,
decoration: InputDecoration(helperText: 'Optional'),
onChanged: (newValue) {
_columnId = newValue;
},
),
);
},
);
}
This is a duplicate of a widget in the same form for a user dropdown select. The original widget (in the same page) is the following:
Widget _ownerSelect() {
return FutureBuilder(
future: userProvider.getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<DropdownMenuItem<String>> usernameList = [];
if (snapshot.hasData) {
usernameList.add(DropdownMenuItem<String>(
child: Text('Select Owner'), value: 0.toString()));
_users = snapshot.data;
} else {
usernameList.add(DropdownMenuItem<String>(
child: Text('Loading..'), value: 0.toString()));
}
_users.forEach((user) {
usernameList.add(DropdownMenuItem<String>(
child: Container(
child: Text(
user.name,
),
),
value: user.id.toString()));
});
return Container(
// margin: EdgeInsets.only(left: 40.0),
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DropdownButtonFormField(
icon: Padding(
padding: const EdgeInsets.only(right: 12),
child: Icon(Icons.person, color: Colors.blue),
),
items: usernameList,
value: _ownerId,
decoration: InputDecoration(helperText: 'Optional'),
onChanged: (newValue) {
_ownerId = newValue;
},
),
);
},
);
}
For some reason, the "_columnSelect" AsyncSnapshot is null always, even when the Future method is working fine:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:kanboard/src/models/column_model.dart';
import 'package:kanboard/src/preferences/user_preferences.dart';
class ColumnProvider {
final _prefs = new UserPreferences();
Future<List<ColumnModel>> getColumns(projectId) async {
final Map<String, dynamic> parameters = {
"jsonrpc": "2.0",
"method": "getColumns",
"id": 887036325,
"params": {"project_id": projectId}
};
final credentials = "${_prefs.username}:${_prefs.password}";
Codec<String, String> stringToBase64 = utf8.fuse(base64);
String encoded = stringToBase64.encode(credentials);
final resp = await http.post(
Uri.parse(_prefs.endpoint),
headers: <String, String>{"Authorization": "Basic $encoded"},
body: json.encode(parameters),
);
final decodedData = json.decode(utf8.decode(resp.bodyBytes));
final List<ColumnModel> columns = [];
final List<dynamic> results = decodedData['result'];
if (decodedData == null) return [];
results.forEach((column) {
final columnTemp = ColumnModel.fromJson(column);
columns.add(columnTemp);
});
print(columns);
return columns;
}
}
The output of "print(columns)" returns:
I/flutter ( 9486): [Instance of 'ColumnModel', Instance of 'ColumnModel', Instance of 'ColumnModel', Instance of 'ColumnModel']
I don't know what I'm missing here. The form has 2 users dropdown select (with the original FutureBuilder Widget) which works just fine. The Column widget with the Future Builder is the one with the "null" problem in snapshot.data.
Thank you in advance for your time and support with this!
I just found where the problem was:
In the form page(new Task page), The columnProvider.getColumns(task.projectId)) wasn't executing because the "task.projectId" parameter is a String, but the API needs an int.
I was confused because the method were being called by the previous page (A project Page with all the tasks) and the getColumn's argument was indeed an integer: int.parse(projectId).
The Kanboard API doesn't return an error code if the ID parameter is other than INT with this specific call "getColumns" (for some reason).
Of course, Flutter (or Dart) is waiting for a response from http.post that would never arrive. When comparing the two calls from the two pages, I noticed the difference.
So, in conclusion, I specified the int data type argument in the getColumn definition in order to avoid any confusion:
Future<List<ColumnModel>> getColumns(int projectId) async {
Best Regards!

How to select index and delete it's respectively data from API in flutter?

I'm getting images from API and show them into grid view but the requirement is that I press long on any index of the image,a selected icon should be visible on that index image.
but the problem is that when I press long at any index, the selected icon is visible on all indexes.
ScreenShot:
to resolve this, I made model class, in which there are datatype
first is boolean variable(isSelected) for each index, another is for PhotoDetails which is fetching from API, but unable to handle it with FutureBuilder, because it rebuilds the build method when I performed setState and isSelected becomes false.
Code:
Model class:
class Photos{
PhotoDetail photoDetail;
bool isSelected;
Photos({this.photoDetail, this.isSelected});
}
FutureBuilder:
Expanded(
child: FutureBuilder<PhotoModel>(
future: _photoApi.getPhotosByUserList(
token: widget.tokenId,
contactId: widget.userContent.id,
),
builder:(BuildContext context, AsyncSnapshot<PhotoModel> snapshot){
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError){
return Center(child: new Text('Error: ${snapshot.error}'));
}
List<Photos> photos =[];
snapshot.data.content.forEach((element) {
photos.add(
Photos(
isSelected: false,
photoDetail: element
)
);
});
print("photos photos photos length:${photos.length}");
return photos.length>0?
sliverGridWidget(context,photos)
:Container(
alignment: Alignment.center,
child: Text("Empty"),
);
}
)
)
Images in grid view:
Widget sliverGridWidget(BuildContext context, List<Photos> listPhotoDetail){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: 6,
itemCount: listPhotoDetail.length,
itemBuilder: (context, index){
return InkWell(
onLongPress: (){
setState(() {
enable = true;
print("iinnndexxxxxxx:$index");
// listPhotoDetail[index].isSelected = true;
});
},
child: Container(
alignment: Alignment.bottomRight,
decoration: BoxDecoration(
color:Colors.grey[100],
image: DecorationImage(
image: NetworkImage(listPhotoDetail[index].photoDetail.image.fileUrl),
fit: BoxFit.cover
)
),
child:enable?
Image.asset('assets/icons/selected.png')
:Container()
),
);
},
staggeredTileBuilder: (index)=> view ?StaggeredTile.count(6,6):StaggeredTile.count(2,2),
mainAxisSpacing: 8.0,
crossAxisSpacing:8.0,
);
}
To solve it try to use a specific key for every image

Rebuild cause "Stream has already been listened to"

I have BehaviorSubject that emits a list of items from a Firebase query everytime I type in a TextField, like a full-text search.
I use a StreamBuilder (inside a ListView) to listen to this stream and display all list's items in a Column wrapped with an AnimatedSwitcher.
When I try to scroll the view or when the column refreshes I get "Stream has already been listened to". I tried everything but I was not able to fix it.
Edit: I added some code
view.dart
PostController _postController = new PostController();
ListView(
padding: EdgeInsets.all(12),
children: <Widget>[
//titolo
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: Color.fromRGBO(242, 242, 242, 1),
),
padding: EdgeInsets.all(8),
child: Column(
children: <Widget>[
StreamBuilder(
stream: _postController.titleStream$,
builder: (BuildContext context, AsyncSnapshot<String> snap) {
return TextField(
onChanged: (String search) => _postController.updateTitle(search),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(12),
border: InputBorder.none,
fillColor: Colors.transparent,
filled: true,
hintText: "titolo",
errorText: snap.error
),
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 24),
);
},
),
StreamBuilder(
stream: _postController.superStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snap){
return AnimatedSwitcher(
duration: Duration(milliseconds: 500),
transitionBuilder: (child, animation) => SizeTransition(sizeFactor: animation, child: child, axisAlignment: -1,),
child: snap.hasData ? BuildQuestions(snap.data.documents) : Container(width: 0, height: 0)
);
},
),
postController.dart
BehaviorSubject _titleController = new BehaviorSubject<String>();
Observable<String> get titleStream$ =>
_titleController.stream.transform(_validateTitle); //if string is not empty it emits otherwise it emits an error
String get title => _titleController.value;
void updateTitle(String title) => _titleController.add(title);
Observable<QuerySnapshot> get superStream => Observable.combineLatest2(
titleStream$,
Queries.search(_titleController.value), //search stream from firestore api
(String title, QuerySnapshot snap) => snap
);
SOLVED
I had a submit button inside a StreamBuilder that was listening to another Observable which I use to check if the form's field are not empty. When I type in the TextField and the Column is displayed, the Submit button slide down out of the view, when I scroll down towards it the StreamBuilder is rebuilded and tries to listen to his stream again and this generates the error.
Solved with:
Observable<bool> get validate => Observable.combineLatest2(titleStream$, descriptionStream$, (t, d) => true).asBroadcastStream();
declare your stream as broadcast stream as below
i use rxdart you should modify it with your scenario
static var controller =StreamController<Map<String, dynamic>>();
static var _streamObservable = Observable(controller.stream.asBroadcastStream());