I can't change event status when page opening in flutter - flutter

I am new to FLutter. I am using Bloc pattern as a design pattern.
When clicked a button or text changed I successfully changed the event of the bloc.
But I need to get data when the page opens and bind it to a list.
I don't know how can I change the bloc event to do that?
I've tried to add BlocBuilder in InitState but it didn't work.
here is my code.
class OrderListWidget extends StatefulWidget {
const OrderListWidget({Key? key}) : super(key: key);
#override
_OrderListWidgetState createState() => _OrderListWidgetState();
}
class _OrderListWidgetState extends State<OrderListWidget> {
late List<WorkOrder> workOrderList;
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => WorkOrderBloc(
workOrderRepo: (context).read<WorkOrderRepository>(),
type: WorkOrderType.mt),
child: BlocListener<WorkOrderBloc, WorkOrderState>(
listener: (context, state) {
final formStatus = state.formStatus;
if (formStatus is FormSubmitting) {
LoadingDialog.openLoadingDialog(context, 'Please Wait');
} else {
if (formStatus is! InitialFormStatus) {
LoadingDialog.closeLoadingDialog(context);
}
if (formStatus is SubmissionFailed) {
SnackbarWidget.show(
context, formStatus.exception.toString(), Colors.red);
}
if (formStatus is SubmissionSuccess) {
setState(() {
workOrderList = state.workOrderList!;
});
}
}
},
child: BlocBuilder<WorkOrderBloc, WorkOrderState>(
builder: (context, state) {
return _myListView(context);
},
),
),
);
}
#override
initState() {
context
.read<WorkOrderBloc>()
.add(WorkOrderListing(orderType: WorkOrderType.mt));
super.initState();
}
}
Widget _myListView(BuildContext context) {
var selected = false;
return ListView.builder(
itemCount: workOrderList.length,
itemBuilder: (context, index) {
return Card(
child: CheckboxListTile(
value: selected,
onChanged: (value) {
setState() {
selected = value!;
}
},
title: Text(workOrderList[index].Name),
),
);
},
);
}

I found the way:
return BlocProvider(
create: (context) => WorkOrderBloc(workOrderRepo: WorkOrderRepository())
..add(PickingOrderListing()),
child: BlocListener<WorkOrderBloc, WorkOrderState>(
listener: (context, state) {
....
}
)
using
..add(YOUR EVENT)
after the BlocProvider worked.

Related

how to solve white screen error, when navigator.pop shows white screen for QR scan in flutter. How to do Multiple Scan in flutter?

How to do Multiple Scan in flutter
var passthroughData;
PassthroughQrScanData? passthroughQrScan;
MobileScannerController cameraController = MobileScannerController();
bool _screenOpened = false;
class PassthroughQrScanPage extends StatefulWidget {
final String? schedule_id;
final String? compoundCode;
final String? lotNo;
PassthroughQrScanPage({
this.schedule_id,
this.compoundCode,
this.lotNo,
});
#override
State<StatefulWidget> createState() => PageState();
}
class PageState extends State<PassthroughQrScanPage> {
final ApiRepository repository = ApiRepository(
apiClient: ApiClient(
httpClient: http.Client(),
),
);
#override
void initState() {}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: BlocProvider(
create: (context) => PassthroughqrscanBloc(repository),
child: BlocBuilder<PassthroughqrscanBloc, PassthroughqrscanState>(
builder: (context, state) {
if (state is PassthroughqrscanEmpty) {
return scan(context);
}
if (state is PassthroughqrscanError) {
return ShowErrorMessage(
context, state.error.message.toString());
}
if (state is PassthroughqrscanLoaded) {
String json = jsonEncode(state.entity);
print("------>>>>>>>>>>>D>S>D>>$json");
Prefs().setPassthroughData(json);
// Navigator.pop(context);
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (ctxDialog) => PassDialog(
compoundCode: widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
schedule_id: widget.schedule_id.toString(),
screenClosed: _screenWasClosed));
});
}
return Container();
Center(
child: CircularProgressIndicator(),
);
},
),
),
),
);
}
Widget ShowErrorMessage(BuildContext context, String error) {
print("------------------------------/./././$error");
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error)));
});
return scan(context);
}
void _screenWasClosed() {
_screenOpened = false;
}
scan(BuildContext mcontext) => MobileScanner(onDetect: (barcode, args) {
String code = barcode.rawValue ?? "";
debugPrint('Barcode found! $code');
if (code.isNotEmpty) {
if (!_screenOpened) {
_screenOpened = true;
passthroughData = jsonDecode(code);
passthroughQrScan = PassthroughQrScanData.fromJson(passthroughData);
BlocProvider.of<PassthroughqrscanBloc>(mcontext, listen: false)
..add(VerifyPassthroughBatch(
passthroughQrScan?.operationName ?? "",
widget.schedule_id.toString(),
passthroughQrScan?.transactionId ?? "",
passthroughQrScan?.transactionRange ?? ""));
}
}
});
// void _foundBarcode(Barcode barcode, MobileScannerArguments? args) {
// /// open screen
//
//
// }
}
class PassDialog extends StatefulWidget {
// const PassDialog({Key? key}) : super(key: key);
String? schedule_id;
String? compoundCode;
String? lotNo;
final Function() screenClosed;
PassDialog(
{required this.schedule_id,
required this.compoundCode,
required this.lotNo,
required this.screenClosed});
#override
State<PassDialog> createState() => _PassDialogState();
}
class _PassDialogState extends State<PassDialog> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: 150,
height: 100,
child: AlertDialog(
content: Row(
children: [
ElevatedButton(
onPressed: () {
widget.screenClosed();
Navigator.of(
context,
rootNavigator: true,
).pop(
context,
);
},
child: Text("Continue")),
SizedBox(
width: 10,
),
ElevatedButton(
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => GluePassthroughUploadPage(
id: widget.schedule_id.toString(),
compoundCode: widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
)));
});
},
child: Text("Show Add page")),
],
),
),
);
}
}
Future buildShowDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Center(
child: CircularProgressIndicator(),
);
});
}
After QR scan, bloc activates and shows Alert dialog box, when API call is correct. Then, When I give continue in Alert dialog box navigator.pop is added so pop should show a mobile scanner to scan another QR but it shows White screen why? any camera controller should be activated when I give pop. how to solve this white screen error????

Refresh StatefulBuilder Dialog without using onPressed

I need to update the text of my dialog while my report is loading. setState doest not work here.
class ReportW extends StatefulWidget {
const ReportW({Key key}) : super(key: key);
#override
_ReportWState createState() => _ReportWState();
}
class _ReportWState extends State<ReportMenuDownloadW> {
String loadingText;
void updateLoadingText(text){
setState(() {loadingText = text;});
}
#override
Widget build(BuildContext context) {
return MyWidget(
label:REPORT_LABEL,
onTap: () async {
showDialog(context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);});
});
await loadPDF(context,updateLoadingText);
Navigator.pop(context);
},
);
}
}
Is there an alternative solution if it is not possible ? I just need a progress text indicator over my screen while loading.
In your case you can use GlobalKey. For your code:
Define globalKey inside your widget:
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
Set globalKey for your StatefulBuilder:
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
Now you can update UI of your dialog like this:
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
Pay attention, you get unexpected behavior if user will close dialog manually.
How to prevent closing dialog by user: in showDialog use barrierDismissible: false and also wrap your dialog to WillPopScope with onWillPop: () async {return false;}
Possible question:
Why we check _dialogKey.currentState != null?
Because opening dialog and set globalKey take some time and while it's not opened currentState is null. If updateLoadingText will be call before dialog will be open, we shouldn't update UI for dialog.
Full code of your widget:
class OriginalHomePage extends StatefulWidget {
OriginalHomePage({Key? key}) : super(key: key);
#override
_OriginalHomePageState createState() => _OriginalHomePageState();
}
class _OriginalHomePageState extends State<OriginalHomePage> {
String loadingText = "Start";
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
},
);
await loadPDF(context, updateLoadingText);
Navigator.pop(context);
},
child: Text("Open"),
);
}
}
Also i rewrote your code a bit, it seems to me more correct:
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text("Open"),
onPressed: () => _showDialog(),
),
),
);
}
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
// Text for update in dialog
String _loadingText = "Start";
_showDialog() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Padding(
padding: EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
Text(_loadingText),
],
),
),
);
},
),
);
},
);
// Call some function from service
await myLoadPDF(context, _setStateDialog);
// Close dialog
Navigator.pop(context);
}
// Update dialog
_setStateDialog(String newText) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
_loadingText = newText;
});
}
}
}
Result:
Updated dialog

how to trigger search automatically when using SearchDelegate buildSuggestions in flutter

Now I am using SearchDelegate in flutter 2.0.1, this is my buildSuggestions code:
#override
Widget buildSuggestions(BuildContext context) {
var channelRequest = new ChannelRequest(pageNum: 1, pageSize: 10, name: query);
if (query.isEmpty) {
return Container();
}
return FutureBuilder(
future: ChannelAction.fetchSuggestion(channelRequest),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<ChannelSuggestion> suggestions = snapshot.data;
return buildSuggestionComponent(suggestions, context);
} else {
return Text("");
}
});
}
Widget buildSuggestionComponent(List<ChannelSuggestion> suggestions, BuildContext context) {
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${suggestions[index].name}'),
onTap: () async {
query = '${suggestions[index].name}';
},
);
},
);
}
when select the recommand text, I want to automatically trigger search event(when I click the suggestion text, trigger the search, fetch data from server side and render the result to UI) so I do not need to click search button. this is my search code:
#override
Widget buildResults(BuildContext context) {
var channelRequest = new ChannelRequest(pageNum: 1, pageSize: 10, name: query);
return buildResultImpl(channelRequest);
}
Widget buildResultImpl(ChannelRequest channelRequest) {
return FutureBuilder(
future: ChannelAction.searchChannel(channelRequest),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Channel> channels = snapshot.data;
return buildResultsComponent(channels, context);
} else {
return Text("");
}
return Center(child: CircularProgressIndicator());
});
}
what should I do to implement it? I have tried invoke buildResults function in buildSuggestionComponent but it seems not work.
To update the data based on the query, you can make an API call to get the result when clicking on a suggestion, then use a StreamController to stream the results to the buildResults() method and call showResults().
I'm creating a simple app here for demonstration:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _controller = StreamController.broadcast();
#override
dispose() {
super.dispose();
_controller.close();
}
Future<void> _showSearch() async {
await showSearch(
context: context,
delegate: TheSearch(context: context, controller: _controller),
query: "any query",
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Search Demo"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: _showSearch,
),
],
),
);
}
}
class TheSearch extends SearchDelegate<String> {
TheSearch({this.context, this.controller});
BuildContext context;
StreamController controller;
final suggestions =
List<String>.generate(10, (index) => 'Suggestion ${index + 1}');
#override
List<Widget> buildActions(BuildContext context) {
return [IconButton(icon: Icon(Icons.clear), onPressed: () => query = "")];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
return StreamBuilder(
stream: controller.stream,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(
child: Text('Empty result'),
));
return Column(
children: List<Widget>.generate(
snapshot.data.length,
(index) => ListTile(
onTap: () => close(context, snapshot.data[index]),
title: Text(snapshot.data[index]),
),
),
);
},
);
}
#override
Widget buildSuggestions(BuildContext context) {
final _suggestions = query.isEmpty ? suggestions : [];
return ListView.builder(
itemCount: _suggestions.length,
itemBuilder: (content, index) => ListTile(
onTap: () {
query = _suggestions[index];
// Make your API call to get the result
// Here I'm using a sample result
controller.add(sampleResult);
showResults(context);
},
title: Text(_suggestions[index])),
);
}
}
final List<String> sampleResult =
List<String>.generate(10, (index) => 'Result ${index + 1}');
I have done it through a simple workaround
Simply add this line after your database call
query = query
But be careful of the call looping

How to pass Future<Either<Response, List<dynamic>>> function as parameter to Widget?

I created a general ListPage as below:
class ListPage extends StatelessWidget {
final context;
final Future<Either<Response, List<dynamic>>> futureFuncion;
final classListItem, classDetailPage;
const ListPage(this.context,this.futureFuncion,this.classListItem,this.classDetailPage, {Key key,}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10.0),
child: Column(children: [Expanded(child: _getData())]));
}
FutureBuilder _getData() {
return FutureBuilder<Either<ResponseError, List>>(
future: futureFuncion,
builder: (BuildContext context, AsyncSnapshot<Either<ResponseError, List>> snapshot) {
if (snapshot.hasError) {
return globals.showSnapshotError(snapshot);
} else if (!snapshot.hasData) {
return globals.showLoading('Loading...');
} else {
return snapshot.data.fold((l) {
return Error().showError(l.error, l.errorDescription);
}, (r) {
return ListView(children: _buildList(context, r));
});
}
},
);
}
}
I use code as below to create List Page:
class RequestListPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
String url = globals.debug ? 'assets/data/list.json' : 'domain.com/list';
return Scaffold(
appBar: AppBar(toolbarHeight: 50,centerTitle: true,title: Text("List")),
body: ListPage(
context,
debug
? LoadDataFromJsonFile().loadListData(context, url, '$Response')
: RequestApi().getListData(url, '$Response'),
RequestListItem,
RequestDetailPage,
),
);
}
Widget _buildList(context, list) {
List<Widget> children = [];
list.forEach((item) {
children.add(_buildTile(context, item));
});
return ListView(children: children);
}
ListTile _buildTile(context, item) {
return ListTile(
title: _buildListItem(context, item),
onTap: () => showDetail(context, item),
);
}
Widget _buildListItem(context, item) {
return classListItem;
}
void showDetail(context, item) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => classDetailPage(context, item)),
);
}
}
}
When I run this code, I got error as below, what can I do?
Attempted to use type 'RequestListItem' as a function. Since types do not define a method 'call', this is not possible. Did you intend to call the RequestListItem constructor and forget the 'new' operator?
Receiver: RequestListItem
Tried calling: RequestListItem(Instance of 'StatefulElement', Instance of 'Response')

Provider.of<> returning null with ChangeNotifierProxyProvider?

In my test code below I have a flag that determines whether to use a ChangeNotifierProvider or a ChangeNotifierProxyProvider. When I press the RaisedButton both approaches properly display my GroupEditorPage.
const isUsingChangeNotifierProxyProvider = true;
class GroupsPage extends StatelessWidget {
showGroupEditor(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(builder: (_) {
return isUsingChangeNotifierProxyProvider
? ChangeNotifierProxyProvider<CloudServicesProvider,
GroupEditorProvider>(
create: (_) => GroupEditorProvider(),
update: (_, cloudServicesProvider, groupEditorProvider) =>
groupEditorProvider.update(cloudServicesProvider),
child: GroupEditorPage(),
)
: ChangeNotifierProvider<GroupEditorProvider>(
create: (_) => GroupEditorProvider(),
child: GroupEditorPage(),
);
}),
);
}
#override
Widget build(BuildContext context) {
return SliversPage(
text: 'Testing',
sliverList: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return RaisedButton(
child: Text('+Create Group'),
onPressed: () => showGroupEditor(context),
);
},
childCount: 1,
),
),
);
}
}
But Provider.of only returns my GroupEditorProvider instance when ChangeNotifierProvider is used. When Change ChangeNotifierProxyProvider is used, groupEditorProvider below is null.
class GroupEditorPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final groupEditorProvider = Provider.of<GroupEditorProvider>(context);
I've been using Provider for some time but am new to ChangeNotifierProxyProvider so likely not understanding something fundamental.
Turns out I wasn't returning the provider instance from my GroupEditorProvider.update function:
update(CloudServicesProvider cloudServicesProvider) {
if (_cloudServicesProvider == null) {
this._cloudServicesProvider = cloudServicesProvider;
}
return this; // <--- was missing
}
Should Flutter have thrown an exception for this? I'll post to github if so.