Flutter fetch widget by status not working - flutter

I wrote a code like this:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sms_inbox/flutter_sms_inbox.dart';
class Inbox extends StatefulWidget {
const Inbox({Key? key}) : super(key: key);
#override
State<Inbox> createState() => _InboxState();
}
SmsQuery query = new SmsQuery();
List<SmsMessage> AllMessages = [];
Timer? _timer;
class _InboxState extends State<Inbox> {
#override
void initState() {
GetAllMessages();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
GetAllMessages();
print("refreshed");
print(AllMessages); // output: []
});
super.initState();
}
Future<void> GetAllMessages() async {
Future.delayed(Duration.zero, () async {
List<SmsMessage> messages = await query.querySms(
kinds: [SmsQueryKind.inbox],
count: 50,
);
setState(() {
AllMessages = messages;
});
});
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Page(),
),
);
}
}
Widget Page() {
if (AllMessages == null) {
return Text("No message");
} else {
Column(
children: [
const SizedBox(height: 25),
Container(
child: Column(
children: AllMessages.map((msg) {
return Container(
child: Card(
child: ListTile(
leading: Text("${msg.body}"),
title: Text("${msg.address}"),
),
),
);
}
).toList(),
),
),
],
);
}
return SizedBox();
}
I'm trying to access and list SMS's on the phone. I save the SMS in the list called AllMessages. If there is no SMS, I want it to show a Text named No message but it doesn't.
It shows blank as in the picture:
Why could this be? How can I solve the problem? Thanks in advance for your help.

Related

Dart - execute a function after x seconds unless cancelled by event

I am currently writing an app using Flutter and Dart. On a button onPressed event I would like to invoke an action that executes after timeLeft seconds unless it is cancelled by correctly entering a pin. Additionally, I would like to use the value timeLeft in a Text widget.
This would require a structure with the following functionality:
executing a function after an x amount of seconds
this function should execute unless some event (e.g. entering a pin correctly) has occurred.
the timeLeft value should be accessible to be used in a Text widget and should update as the timer progresses.
I am wondering how to do this according to flutter's and dart's best practices. For state management I am using the provider pattern so preferably this approach is compatible with the provider pattern.
This is what I have tried so far:
class Home extends ChangeNotifier {
int secondsLeft = 10;
void onPressedEmergencyButton(BuildContext context) {
countDown();
showDialog<void>(
context: context,
builder: (context) {
return ScreenLock(
title: Text(
"Sending message in ${context.read<Home>().secondsLeft} seconds"),
correctString: '1234',
canCancel: false,
didUnlocked: () {
Navigator.pop(context);
},
);
},
);
}
void countDown() {
Future.delayed(const Duration(seconds: 1), () {
secondsLeft =- 1;
notifyListeners();
if (secondsLeft <= 0) {
// Do something
return;
}
});
}
}
You can use CancelableOperation from async package.
Simplifying code-snippet and about _cancelTimer(bool) , this bool used to tell widget about true = time end, and on cancel false like _cancelTimer(false);, rest are described on code-comments.
class TS extends StatefulWidget {
const TS({Key? key}) : super(key: key);
#override
State<TS> createState() => _TSState();
}
class _TSState extends State<TS> {
Timer? _timer;
final Duration _refreseRate = const Duration(seconds: 1);
CancelableOperation? _cancelableOperation;
Duration taskDuration = const Duration(seconds: 5);
bool isSuccess = false;
_initTimer() {
if (_cancelableOperation != null) {
_cancelTimer(false);
}
_cancelableOperation = CancelableOperation.fromFuture(
Future.delayed(Duration.zero),
).then((p0) {
_timer = Timer.periodic(_refreseRate, (timer) {
setState(() {
taskDuration -= _refreseRate;
});
if (taskDuration <= Duration.zero) {
/// task complete on end of duration
_cancelTimer(true);
}
});
}, onCancel: () {
_timer?.cancel();
setState(() {});
});
}
_cancelTimer(bool eofT) {
// cancel and reset everything
_cancelableOperation?.cancel();
_timer?.cancel();
_timer = null;
taskDuration = const Duration(seconds: 5);
isSuccess = eofT;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (isSuccess)
Container(
height: 100,
width: 100,
color: Colors.green,
),
if (_timer != null)
Text("${taskDuration.inSeconds}")
else
const Text("init Timer"),
],
),
),
floatingActionButton: Row(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
child: const Text("init"),
onPressed: () {
_initTimer();
},
),
FloatingActionButton(
child: const Text("Cancel"),
onPressed: () {
_cancelTimer(false);
},
),
],
),
);
}
}
You can use the Timer class to run a function after a set Duration. It doesn't give you the time remaining, but you can calculate it yourself.
Here is a quick implementation I put together:
import 'dart:async';
import 'package:flutter/material.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
home: const Scaffold(
body: Center(
child: Countdown(),
),
),
);
}
}
class Countdown extends StatefulWidget {
const Countdown({Key? key}) : super(key: key);
#override
_CountdownState createState() => _CountdownState();
}
class _CountdownState extends State<Countdown> {
bool active = false;
Timer? timer;
Timer? refresh;
Stopwatch stopwatch = Stopwatch();
Duration duration = const Duration(seconds: 5);
_CountdownState() {
// this is just so the time remaining text is updated
refresh = Timer.periodic(
const Duration(milliseconds: 100), (_) => setState(() {}));
}
void start() {
setState(() {
active = true;
timer = Timer(duration, () {
stop();
onCountdownComplete();
});
stopwatch
..reset()
..start();
});
}
void stop() {
setState(() {
active = false;
timer?.cancel();
stopwatch.stop();
});
}
void onCountdownComplete() {
showDialog(
context: context,
builder: (context) => const AlertDialog(
title: Text('Countdown was not stopped!'),
),
);
}
int secondsRemaining() {
return duration.inSeconds - stopwatch.elapsed.inSeconds;
}
#override
void dispose() {
timer?.cancel();
refresh?.cancel();
stopwatch.stop();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (active) Text(secondsRemaining().toString()),
if (active)
TextButton(onPressed: stop, child: const Text('Stop'))
else
TextButton(onPressed: start, child: const Text('Start')),
],
);
}
}

How to call a function inside a StatefulWidget from another StatefulWidget?

I come here asking for help to understand if it is possible to put one or several functions outside a StatefulWidget followig an example that I found in the internet.
The following code example is working and it is to connect over Wifi to a ESP32 Microcontroller.
Inside the StatefulWidget there are some functions like for example void channelconnect(), void initState() etc. My question is how I can put these functions outside the StatefulWidget in another Widget. Is it possible to seperate the Logic from the UI?
void main() => runApp(
const GetMaterialApp(home: MyApp()),
);
final key = GlobalKey<WebSocketLed1State>();
//----------------------------------------------------------------
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: WebSocketLed1(),
);
}
}
//---------------------------------------------------------------------------
class WebSocketLed1 extends StatefulWidget {
const WebSocketLed1({Key? key}) : super(key: key);
#override
State createState() => WebSocketLed1State();
}
class WebSocketLed1State extends State<WebSocketLed1> {
late bool ledstatus; //boolean value to track LED status, if its ON or OFF
late IOWebSocketChannel channel;
late bool connected; //boolean value to track if WebSocket is connected
var val = -255.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("LED - ON/OFF NodeMCU"),
backgroundColor: Colors.redAccent),
body: Container(
alignment: Alignment.topCenter, //inner widget alignment to center
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
child: connected
? const Text("WEBSOCKET: CONNECTED")
: const Text("DISCONNECTED")),
Container(
child: ledstatus
? const Text("LED IS: ON")
: const Text("LED IS: OFF")),
Container(
margin: const EdgeInsets.only(top: 30),
child: TextButton(
//button to start scanning
onPressed: () {
//on button press
if (ledstatus) {
//if ledstatus is true, then turn off the led
//if led is on, turn off
sendcmd("poweroff");
ledstatus = false;
} else {
//if ledstatus is false, then turn on the led
//if led is off, turn on
sendcmd("poweron");
ledstatus = true;
}
setState(() {});
},
child: ledstatus
? const Text("TURN LED OFF")
: const Text("TURN LED ON"))),
SizedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Brightness: '
'${((255 - val.toInt().abs()) / 255 * 100).toInt()}%',
style: const TextStyle(
color: Colors.black,
fontSize: 24.0,
fontWeight: FontWeight.w500,
),
),
RotatedBox(
quarterTurns: 3,
child: SizedBox(
width: 200,
child: Slider.adaptive(
value: val,
min: -255,
max: 0,
onChanged: sendMessage,
),
),
)
],
),
),
],
),
),
],
)),
);
}
#override
void initState() {
ledstatus = false; //initially leadstatus is off so its FALSE
connected = false; //initially connection status is "NO" so its FALSE
Future.delayed(Duration.zero, () async {
channelconnect(); //connect to WebSocket wth NodeMCU
});
super.initState();
}
void channelconnect() {
//function to connect
try {
channel = IOWebSocketChannel.connect(
"ws://192.168.204.65:81"); //channel IP : Port
channel.stream.listen(
(message) {
// ignore: avoid_print
print(message);
setState(() {
if (message == "connected") {
connected = true; //message is "connected" from NodeMCU
} else if (message == "poweron:success") {
ledstatus = true;
} else if (message == "poweroff:success") {
ledstatus = false;
}
});
},
onDone: () {
//if WebSocket is disconnected
// ignore: avoid_print
print("Web socket is closed");
setState(() {
connected = false;
});
},
onError: (error) {
// ignore: avoid_print
print(error.toString());
},
);
} catch (_) {
// ignore: avoid_print
print("error on connecting to websocket.");
}
}
Future<void> sendcmd(String cmd) async {
if (connected == true) {
if (ledstatus == false && cmd != "poweron" && cmd != "poweroff") {
// ignore: avoid_print
print("Send the valid command");
} else {
channel.sink.add(cmd); //sending Command to NodeMCU
}
} else {
channelconnect();
// ignore: avoid_print
print("Websocket is not connected.");
}
}
void sendMessage(double v) {
try {
setState(() {
val = v.roundToDouble();
});
channel.sink.add('clear\n');
channel.sink.add('${val.toInt().abs()}\n');
} catch (e) {
debugPrint(e.toString());
}
}
}
I suggest you to define a class and define your function in it and then call functions you want any where you want.
Thanks #最白目, thanks #Abraams gtr for your help.
Also thanks #Tomerikoo for making a review on my inicial question. :)
Like i told, it was the first time for me, here asking for help and i am not familiar with stackoverflow.
After searching on the internet for some info for my quetion i could find a solution.
It can be that it is not the best way to make it, but it is working. For sure there is a better way to make it.
I will continuo working on it. Of course if there is somebody with a better method i would appreciate another point of view.
Flutter and Dart are completely new Land for me.
Follow my approach for my inicial question.
void main() {
runApp(
const GetMaterialApp(home: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder(
init: MessageController(),
builder: (GetxController controller) {
return const WebSocketLed();
},
);
}
}
class WebSocketLed extends StatelessWidget {
const WebSocketLed({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Slider'),
),
body: Container(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Brightness1(),
Slider1(),
],
),
));
}
}
class Slider1 extends StatelessWidget {
const Slider1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final messageController = Get.put(MessageController());
return SizedBox(
child: GetBuilder<SliderController>(
init: SliderController(),
builder: (ctrl) => SizedBox(
child: Slider(
value: ctrl.quality,
min: 0,
max: 255,
divisions: 255,
label: ctrl.quality.round().toString(),
onChanged: (double value) {
ctrl.setQuality(value);
},
onChangeEnd: (messageController.sendMessage),
onChangeStart: (messageController.sendMessage)),
),
));
}
}
class Brightness1 extends StatelessWidget {
const Brightness1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
child: GetBuilder<SliderController>(
init: SliderController(),
builder: (ctrl) => SizedBox(
child: Text(
ctrl.quality.round().toString(),
),
)));
}
}
class MessageController extends GetxController {
late IOWebSocketChannel channel;
late bool connected;
var val = 255.0;
void sendMessage(double v) {
try {
val = v.roundToDouble();
channel.sink.add('clear\n');
channel.sink.add('${val.toInt().abs()}\n');
} catch (e) {
debugPrint(e.toString());
}
}
#override
void onInit() {
super.onInit();
try {
channel = IOWebSocketChannel.connect(
"ws://192.168.75.5:81"); //channel IP : Port
channel.stream.listen(
(message) {
// ignore: avoid_print
print(message);
},
onDone: () {
//if WebSocket is disconnected
// ignore: avoid_print
print("Web socket is closed");
},
onError: (error) {
// ignore: avoid_print
print(error.toString());
},
);
} catch (_) {
// ignore: avoid_print
print("error on connecting to websocket.");
}
}
#override
void dispose() {
channel.sink.close();
super.dispose();
}
}
//--------------------------------------------------------------
class SliderController extends GetxController {
static SliderController get to => Get.find();
double quality = 255;
void setQuality(double quality) {
this.quality = quality;
update();
}
}

How To Pass Information From one Screen to another Screen

I wanted to Download a Image with its progress and message. I wanted to show it in a dialog. When ever I click Download Button the Image gets downloaded and the Container pops up, but it does not Show any value.
The below code uses Image_downloader package. Raised button downloads the image and display the Blank Container without any value;
import 'dart:async';
import 'dart:io';
import 'Download.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_downloader/image_downloader.dart';
void main() => runApp(HomePage());
class HomePage extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
String message = "";
String path = "";
int _progress = 0;
#override
void initState() {
super.initState();
ImageDownloader.callback(onProgressUpdate: (String imageId, int progress) {
setState(() {
_progress = progress;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () {
_downloadImage(
"https://images.unsplash.com/photo-1503023345310-bd7c1de61c7d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80",
);
showDialog(
context: context,
builder: (_) => FunkyOverlay(progress: _progress, message: message,),
);
},
child: Text("default destination"),
),
],
),
),
),
);
}
Future<void> _downloadImage(String url,
{AndroidDestinationType destination, bool whenError = false}) async {
String fileName;
String path;
try {
String imageId;
if (whenError) {
imageId = await ImageDownloader.downloadImage(url).catchError((error) {
if (error is PlatformException) {
var path = "";
if (error.code == "404") {
print("Not Found Error.");
} else if (error.code == "unsupported_file") {
print("UnSupported FIle Error.");
path = error.details["unsupported_file_path"];
}
setState(() {
message = error.toString();
path = path;
});
}
print(error);
}).timeout(Duration(seconds: 10), onTimeout: () {
print("timeout");
});
} else {
if (destination == null) {
imageId = await ImageDownloader.downloadImage(url);
} else {
imageId = await ImageDownloader.downloadImage(
url,
destination: destination,
);
}
}
if (imageId == null) {
return;
}
fileName = await ImageDownloader.findName(imageId);
path = await ImageDownloader.findPath(imageId);
} on PlatformException catch (error) {
setState(() {
message = error.message;
});
return;
}
if (!mounted) return;
setState(() {
message = 'Image Downloaded';
});
}
}
This is the Pop up Container Part
import 'package:flutter/material.dart';
class FunkyOverlay extends StatefulWidget {
String message;
int progress;
FunkyOverlay({#required this.message, #required this.progress});
#override
State<StatefulWidget> createState() => FunkyOverlayState(message, progress);
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
String message;
int progress;
FunkyOverlayState(this.message, this.progress);
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Downloaded: $progress'),
Text(message)
],
),
),
),
),
),
);
}
}
You can copy paste run full code below
You can use StreamBuilder to receive progress from onProgressUpdate
class HomePageState extends State<HomePage> {
...
#override
void initState() {
super.initState();
_events = new StreamController<int>.broadcast();;
_events.add(0);
ImageDownloader.callback(onProgressUpdate: (String imageId, int progress) {
setState(() {
print("progress $progress");
_progress = progress;
_events.add(progress);
});
return StreamBuilder<int>(
stream: _events.stream,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Center(
...
children: <Widget>[
Text('Downloaded: ${snapshot.data.toString()}'),
Text(message)
working demo
full code
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:image_downloader/image_downloader.dart';
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
StreamController<int> _events;
class HomePageState extends State<HomePage> {
String message = "";
String path = "";
int _progress = 0;
#override
void initState() {
super.initState();
_events = new StreamController<int>.broadcast();
;
_events.add(0);
ImageDownloader.callback(onProgressUpdate: (String imageId, int progress) {
setState(() {
print("progress $progress");
_progress = progress;
_events.add(progress);
if (progress == 100) {
Navigator.pop(context);
}
});
});
}
#override
void dispose() {
// TODO: implement dispose
_events.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () {
_events.add(0);
_downloadImage(
"https://images.unsplash.com/photo-1503023345310-bd7c1de61c7d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80",
);
showDialog(
context: context,
builder: (_) => FunkyOverlay(
progress: _progress,
message: message,
),
);
},
child: Text("default destination"),
),
],
),
),
);
}
Future<void> _downloadImage(String url,
{AndroidDestinationType destination, bool whenError = false}) async {
String fileName;
String path;
try {
String imageId;
if (whenError) {
imageId = await ImageDownloader.downloadImage(url).catchError((error) {
if (error is PlatformException) {
var path = "";
if (error.code == "404") {
print("Not Found Error.");
} else if (error.code == "unsupported_file") {
print("UnSupported FIle Error.");
path = error.details["unsupported_file_path"];
}
setState(() {
message = error.toString();
path = path;
});
}
print(error);
}).timeout(Duration(seconds: 10), onTimeout: () {
print("timeout");
});
} else {
if (destination == null) {
imageId = await ImageDownloader.downloadImage(url);
} else {
imageId = await ImageDownloader.downloadImage(
url,
destination: destination,
);
}
}
if (imageId == null) {
print("imageId is null");
return;
}
fileName = await ImageDownloader.findName(imageId);
path = await ImageDownloader.findPath(imageId);
} on PlatformException catch (error) {
setState(() {
message = error.message;
});
return;
}
if (!mounted) return;
setState(() {
message = 'Image Downloaded';
});
}
}
class FunkyOverlay extends StatefulWidget {
String message;
int progress;
FunkyOverlay({#required this.message, #required this.progress});
#override
State<StatefulWidget> createState() => FunkyOverlayState(message, progress);
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
String message;
int progress;
FunkyOverlayState(this.message, this.progress);
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
print("StreamBuilder build");
return StreamBuilder<int>(
stream: _events.stream,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
print("snapshot.data ${snapshot.data.toString()}");
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Downloaded: ${snapshot.data.toString()}'),
Text(message)
],
),
),
),
),
),
);
});
}
}

ChipSelect callback is not updating the UI. Flutter

I gave up trying to find the reason setState() is not running build method. I have a ChoiceChip which has a callback function. Debug showed me that I actually receive the selected chip at the callback but setState() is not updating the ui. I have spent all day trying to understand why setState() is not running the build() method. Here is my code
class SocialStoryCategory extends StatefulWidget {
final Function(String) onMenuItemPress;
SocialStoryCategory({Key key, #required this.onMenuItemPress}) : sup er(key: key);
#override
_SocialStoryCategoryState createState() => _SocialStoryCategoryState();
}
class _SocialStoryCategoryState extends State<SocialStoryCategory> {
int _value = 0;
List<String> categoryList;
#override
Widget build(BuildContext context) {
categoryList = [
NoomeeLocalizations.of(context).trans('All'),
NoomeeLocalizations.of(context).trans('Communication'),
NoomeeLocalizations.of(context).trans('Behavioral'),
NoomeeLocalizations.of(context).trans('ADL'),
NoomeeLocalizations.of(context).trans('Other')
];
return Wrap(
spacing: 4,
children: List<Widget>.generate(5, (int index) {
return Theme(
data: ThemeData.from(
colorScheme: ColorScheme.light(primary: Colors.white)),
child: Container(
child: ChoiceChip(
elevation: 3,
selectedColor: Colors.lightBlueAccent,
label: Text(categoryList.elementAt(index)),
selected: _value == index,
onSelected: (bool selected) {
setState(() {
_value = selected ? index : null;
if (categoryList.elementAt(_value) == "All") {
widget.onMenuItemPress("");
} else {
widget.onMenuItemPress(categoryList.elementAt(_value));
}
});
},
),
),
);
}).toList());
}
}
Here is the place where I get the callback
class SocialStoriesHome extends StatefulWidget {
#override
_SocialStoriesHomeState createState() => _SocialStoriesHomeState();
}
class _SocialStoriesHomeState extends State<SocialStoriesHome>
with TickerProviderStateMixin {
String title;
TabController _tabController;
int _activeTabIndex = 0;
String _defaultStoryCategory;
_goToDetailsPage() {
Navigator.of(context).pushNamed("parent/storyDetails");
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 2);
_defaultStoryCategory = '';
}
#override
Widget build(BuildContext context) {
return BaseWidget<SocialStoryViewModel>(
model: new SocialStoryViewModel(
socialStoriesService: Provider.of(context),
),
onModelReady: (model) =>
model.fetchDefaultStoriesByCategory(_defaultStoryCategory),
builder: (context, model, child) => DefaultTabController(
length: 2,
child: Scaffold(
body: model.busy
? Center(child: CircularProgressIndicator())
: Container(
child: Column(
children: <Widget>[
new SocialStoryCategory(
onMenuItemPress: (selection) {
setState(() {
_defaultStoryCategory = selection;
});
},
),
Expanded(
child: ListView(
children: getStories(model.socialStories),
),
),
],
),
),
);
}
}
List<Widget> getStories(List<SocialStoryModel> storyList) {
List<Widget> list = List<Widget>();
for (int i = 0; i < storyList.length; i++) {
list.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Template(
title: storyList[i].name,
subTitle: storyList[i].categories,
hadEditIcon: false),
));
}
return list;
}
Finally I have found the solution.
I have simply replaced
new SocialStoryCategory(onMenuItemPress: (selection) {
setState(() {
_defaultStoryCategory = selection;
});
},
),
to
new SocialStoryCategory(
onMenuItemPress: (selection) {
model.fetchDefaultStoriesByCategory(selection);
},
),
my viewModel extend change notifier and build a child as consumer so I totally understand why it works. But I still do not understand why the previous version was not working. I will feel happy again only when you explain me the problem,

Flutter - How set double value to Bloc?

In my application there is a field for input of a certain value in double format, but this is not working.
What am I doing wrong?
My Bloc
class BudgetBloc extends BlocBase {
String _documentId;
double _movimentos;
BudgetBloc() {
_movimentosController.listen((value) => _movimentos = value);
}
void setBudget(Budget budget) {
_documentId = budget.documentId();
setMovimentos(budget.movimentos);
}
var _movimentosController = BehaviorSubject<double>();
Stream<double> get outMovimentos => _movimentosController.stream;
void setMovimentos(double value) => _movimentosController.sink.add(value);
bool insertOrUpdate() {
var budget = Budget()
..movimentos = _movimentos;
if (_documentId?.isEmpty ?? true) {
_repository.add(budget);
} else {
_repository.update(_documentId, budget);
}
return true;
}
#override
void dispose() {
_movimentosController.close();
super.dispose();
}
}
My BudgetPage
class BudgetPage extends StatefulWidget {
BudgetPage(this.budget);
final Budget budget;
#override
_BudgetPageState createState() => _BudgetPageState();
}
class _BudgetPageState extends State<BudgetPage> {
TextEditingController _movimentosController;
final _bloc = BudgetBloc();
#override
void initState() {
_movimentosController =
TextEditingController(text: widget.budget.movimentos.toString());
super.initState();
}
#override
void dispose() {
_movimentosController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add Movimento"),
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
Container(
child: TextField(
decoration: InputDecoration(labelText: "Movimento"),
controller: _movimentosController,
onChanged: _bloc.setMovimentos,
),
),
Container(
height: 20,
),
RaisedButton(
child: Text("Save"),
onPressed: () {
if (_bloc.insertOrUpdate()) {
Navigator.pop(context);
}
},
)
],
),
),
),
);
}
}
Thanks
Function(double) can't be assigned to the parameter type void Function(String)
The error is telling you that you are providing a String when it expects a double.
I would try passing a double to the BLoC, something like this:
onChanged: (value) => _bloc.setMovimentos(double.parse(value)),