Why my app (developed using ChangeNotifierProvider) updates with one level delay? - flutter

I'm developing a flutter app and I've used ChangeNotifierProvider to manage states.I have a class called 'Data' which is the model of the app (model in MVC design pattern) and a class called 'DataManager' which is the controller of the app (controller in MVC design pattern) and makes an instance of Data in it.
I've made instance of ChangeNotifierProvider in my main and the ChangeNotifier is DataManager. So the methods in DataManager call notifyListeners() method.
When I run the app, I add a tile and the UI it won't change although the tile is added. After I add another tile the first one appears and so on.The app is always one level behind the user.
Can you help me fix this problem?
This is main.dart:
main(){
runApp(MyApp());
}
class MyApp extends StatefulWidget{
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => DataManager(),
child: MaterialApp(
home: LoadingScreen()
),
);
}
}
This is Data.dart (It's methods might not be important here):
class Data{
Position _location;
List<CityTile> _cityWidgets = List<CityTile>();
List<Weather> _weatherDatas = List<Weather>();
Future<Position> getLocation() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
print('Location services are disabled.');
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.deniedForever) {
print('Location permissions are permanently denied, we cannot request permissions.');
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission != LocationPermission.whileInUse &&
permission != LocationPermission.always) {
print( 'Location permissions are denied (actual value: $permission).');
return Future.error(
'Location permissions are denied (actual value: $permission).');
}
}
_location = await Geolocator.getCurrentPosition();
return _location;
}
void addCity({Weather cityWeather}) async{
bool isReady = await cityWeather.updateWeather();
String cityName = cityWeather.getCity;
if(!cityExists(cityName) && isReady) {
_weatherDatas.add(cityWeather);
_cityWidgets.add(CityTile(cityName));
}
// print("widgets:");
// for(CityTile cityTile in _cityWidgets){
// print("${cityTile.city} widget exists");
// }
// print("weathers:");
// for(Weather weather in _weatherDatas){
// print("${weather.getCity} weather exists");
// }
}
Weather searchWeather({String cityName}){
for(Weather weather in _weatherDatas){
if(weather.getCity == cityName){
return weather;
}
}
return null;
}
bool cityExists(String cityName){
if(searchWeather(cityName: cityName) == null)
return false;
else
return true;
}
void removeCity({String cityName}) {
if (cityExists(cityName)) {
_removeCityWidget(cityName: cityName);
_removeCityWeather(cityName: cityName);
}
}
void _removeCityWidget({String cityName}){
CityTile cityTileToRemove;
for(CityTile cityTile in _cityWidgets){
if(cityTile.city == cityName){
cityTileToRemove = cityTile;
}
}
if(cityTileToRemove != null)
_cityWidgets.remove(cityTileToRemove);
}
void _removeCityWeather({String cityName}){
Weather weather = searchWeather(cityName: cityName);
if(weather != null)
_weatherDatas.remove(weather);
}
int widgetNumbers(){
return _cityWidgets.length;
}
get weatherDatas{
return List.unmodifiable(_weatherDatas);
}
get cityWidgets{
return List.unmodifiable(_cityWidgets);
}
}
This is DataManager.dart:
class DataManager extends ChangeNotifier{
Data data = Data();
Future<bool> findWeatherByLocation() async{
Position location = await data.getLocation();
// print("long : ${location.longitude} and lat : ${location.latitude}");
Weather weatherOfHere = Weather(city: null);
String weatherCast = "";
if(location == null){
// print("location is null");
return false;
}
for(int i=0; i<5; i++){
weatherCast = await weatherOfHere.getCurrentWeather(location: location);
if(weatherCast.isNotEmpty)
break;
}
if( weatherCast.isEmpty || jsonDecode(weatherCast)['cod'] == '404') {
// print("city not found");
return false;
}
// print("weathercast : $weatherCast");
addCityByWeather(weatherOfHere);
return true;
}
void addCityByWeather(Weather cityWeather){
data.addCity(cityWeather: cityWeather);
notifyListeners();
}
void addCityByName(String city) async{
if(!data.cityExists(city) && city.isNotEmpty){
Weather cityWeather = Weather(city: city);
bool isRealCity = await cityWeather.updateWeather();
if(isRealCity) {
data.addCity(cityWeather: cityWeather);
}
}
notifyListeners();
}
void removeCity(String city){
data.removeCity(cityName: city);
notifyListeners();
}
int cityNumbers(){
return data.widgetNumbers();
}
Future<bool> updateWeather(String city) async{
Weather weatherToUpdate = data.searchWeather(cityName: city);
bool isUpdated = false;
if(weatherToUpdate == null){
return false;
}
else{
isUpdated = await weatherToUpdate.updateWeather();
notifyListeners();
}
return isUpdated;
}
get weatherDatas{
return data.weatherDatas;
}
get cityWidgets{
return data.cityWidgets;
}
void addOption(String option){
option = option.toLowerCase() == 'feels like' ? 'feels_like' : option;
options[option.toLowerCase()] = true;
//updateAll();
notifyListeners();
}
void removeOption(String option){
option = option.toLowerCase() == 'feels like' ? 'feels_like' : option;
options[option.toLowerCase()] = false;
// updateAll();
notifyListeners();
}
void updateAll(){
for(Weather weather in data.weatherDatas)
weather.updateWeather();
notifyListeners();
}
bool isOptionSelected(String option){
option = option.toLowerCase() == 'feels like' ? 'feels_like' : option;
// print("in isOptionSelected: ${options[option.toLowerCase()]}");
return options[option.toLowerCase()];
}
Color getOptionButtonColor(String option){
option = option.toLowerCase() == 'feels like' ? 'feels_like' : option;
return isOptionSelected(option) ? Colors.indigo : Colors.black38;
}
get getOptions{
return options;
}
String getWeatherScreenPicture(String city){
Weather weatherData = data.searchWeather(cityName: city);
int id = weatherData.id;
if(id == 800){
var now = new DateTime.now();
List clearSky = codeToPicture[800];
if( now.hour> 18 ) {
return clearSky[1];
}else
return clearSky[0];
}
return codeToPicture[id];
}
String getWeatherInfo(String city, String field){
Weather weather = data.searchWeather(cityName: city);
if(weather != null){
switch(field){
case 'temperature':
return weather.temperature;
case 'pressure':
return weather.pressure;
case 'humidity':
return weather.humidity;
case 'weather description':
return weather.weatherDescription;
case 'wind speed':
return weather.windSpeed;
case 'feels_like':
return weather.feelsLike;
}
}
return "null";
}
IconData getWeatherIcon(String city){
Weather weather = data.searchWeather(cityName: city);
if(weather != null)
return weather.icon;
else
return WeatherIcons.refresh;
}
}
There is also a listView.Builder which adds these tiles( city widgets ):
class CitiesScreen extends StatelessWidget {
final TextEditingController _textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: NavigationBar(),
backgroundColor: Colors.lightBlue,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: Column(
children: <Widget>[
ColorfulBox(
ListTile(
title: TextField(
controller: _textEditingController,
style: TextStyle(fontSize: 20, color: Colors.white),),
trailing: SizedBox(
width: 100,
child: Row(
children: [
SizedBox(
width: 50,
child: FlatButton(
child: Icon(Icons.add, size: 30, color: Colors.white,),
onPressed: () {
Provider.of<DataManager>(context, listen: false).addCityByName(_textEditingController.text);
_textEditingController.clear();
},
),
),
SizedBox(
width: 50,
child: FlatButton(
onPressed: () => Provider.of<DataManager>(context, listen: false).findWeatherByLocation(),
child: Icon(Icons.location_on_outlined, size: 30, color: Colors.white,),
),
)
],
),
),
),
),
SizedBox(height: 30,),
Expanded(
child: Consumer<DataManager>(
builder: (context, data, child){
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: Provider.of<DataManager>(context).cityNumbers(),
itemBuilder: (context, index) => Provider.of<DataManager>(context).cityWidgets[index],
);
},
),
)
],
),
),
),
);
}
}

Insert
Provider.of<DataManager>(context);
to build Function.
It listens when you call notifyListeners() and updates the UI.

Related

Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'

I am using [background_locator_2][1] plugin, However when I run it with some modification I get this error
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'
This is the code i am using
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool? isRunning;
LocationDto? lastLocation;
#override
void initState() {
super.initState();
if (IsolateNameServer.lookupPortByName(
LocationServiceRepository.isolateName) !=
null) {
IsolateNameServer.removePortNameMapping(
LocationServiceRepository.isolateName);
}
IsolateNameServer.registerPortWithName(
port.sendPort, LocationServiceRepository.isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
}
#override
void dispose() {
super.dispose();
}
Future<void> updateUI(LocationDto data) async {
final log = await FileManager.readLogFile();
await _updateNotificationText(data);
setState(() {
lastLocation = data;
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await FileManager.readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
FileManager.clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning!) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
//if (await isLocationAlwaysGranted()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
// } else {
// show error
}
}
Future<bool> isLocationAlwaysGranted() async =>
await Permission.locationAlways.isGranted;
/// Tries to ask for "location always" permissions from the user.
/// Returns `true` if successful, `false` othervise.
Future<bool> askForLocationAlwaysPermission() async {
bool granted = await Permission.locationAlways.isGranted;
if (!granted) {
granted =
await Permission.locationAlways.request() == PermissionStatus.granted;
}
return granted;
}
Future<void> _startLocator() async {
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(
LocationCallbackHandler.callback,
initCallback: LocationCallbackHandler.initCallback,
initDataCallback: data,
disposeCallback: LocationCallbackHandler.disposeCallback,
iosSettings: IOSSettings(
accuracy: LocationAccuracy.NAVIGATION,
distanceFilter: 0,
stopWithTerminate: true),
autoStop: false,
androidSettings: AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback:
LocationCallbackHandler.notificationCallback)));
}
The error is in this line under initState when I start or stop the plugin.
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
The original code didn't have null safety so i tried to modify it. However it is pretty evident my knowledge is limited.
[1]: https://pub.dev/packages/background_locator_2
the data you are listening to from the port (dynamic data) can be null and you are sending it to the function updateUI which does not accept nullable type.
you can either check if the data is not null before calling await updateUI(data); or you can make the function updateUI accepts null, i.e (Future<void> updateUI(LocationDto? data) async {) and handle the case that the data is nullable inside the function

Navigation does not reload instantly on Dart/ Flutter

I am working on this navigation that is a list but it is very inconstant in refreshing. Sometimes it refreshes and appears the new button, and other times it does not show the new button. Have you had this problem before.
This is my page showing the routers list and the pages list:
class RouterTabletPage extends StatefulWidget { // ignore: must_be_immutable
BuildContext? context;
RouterTabletPage({Key? key}) : super(key: key);
#override
_RouterTabletPageState createState() => _RouterTabletPageState();
}
class _RouterTabletPageState extends State<RouterTabletPage> with SingleTickerProviderStateMixin {
final NavigationController _navigationController = IoC.get<NavigationController>()!;
final TopBarWidgetController _topBarController = IoC.get<TopBarWidgetController>()!;
final TerminalLockController _terminalLockController = IoC.get<TerminalLockController>()!;
final PreAlarmController _preAlarmController = IoC.get<PreAlarmController>()!;
final UpdateUiService _updateUiService = IoC.get<UpdateUiService>()!;
PageController? _pageViewController;
#override
initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) async {
await _navigationController.initRoutes();
_navigationController.pageViewController = PageController(initialPage: _navigationController.menuSelectedIndex);
_pageViewController = _navigationController.pageController;
await _navigationController.getTabs(_navigationController.menuSelectedIndex);
await _navigationController.setListenerPage();
_topBarController.initTopBar();
await _navigationController.refreshCycleBtn();
});
}
registerContext() {
widget.context = context;
_terminalLockController.registerContext(context);
_preAlarmController.registerContext(context);
_updateUiService.registerContext(context);
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
registerContext();
return Observer(
builder: (_) {
return Scaffold(
appBar: _topBarController.topBarWidget,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
color: Theme.of(context).primaryColor,
margin: const EdgeInsets.all(0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
width: 60,
margin: const EdgeInsets.only(top: 0.0),
color: Theme.of(context).primaryColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: _navigationController.tabs,
),
),
Expanded(
child: Container(
color: Theme.of(context).backgroundColor,
child: PageView(
scrollDirection: Axis.vertical,
controller: _pageViewController,
children: _navigationController.pages,
),
And this is my controller:
#observable
Map<num, PageWithRouter> routers = {};
#observable
String router2 = "";
#observable
int menuSelectedIndex = 0;
#observable
String preloader = "";
#observable
Object? arguments;
#observable
List<Widget> pages = [];
#observable
PageController? pageController;
#observable
List<Widget> tabs = [];
String pathPage = "";
#observable
bool cycleEnable = true;
#observable
bool isCycleStarted = false;
#observable
BuildContext? context;
set pageViewController(PageController _pageController) =>
pageController = _pageController;
#action
Future setRouter(String path) async {
initRoutes();
if (router2 != path) {
pathPage = path;
if (path != pathSplashScreen && path != "") router2 = path;
if (router2 != pathRegister) {
await _treatAuth().then((value) async {
if (!value) {
setMenuSelectedByPath(path);
await initPages();
}
});
}
}
}
#action
Future refreshCurrentPage(List<String> path) async {
if (path.contains(router2)) {
await _treatAuth().then((value) async {
if (!value) {
await initPages();
}
});
}
}
#action
setRouterByHomePage(String path) async {
await setRouter(path);
// log('path'+path);
pageController!.jumpToPage(menuSelectedIndex);
}
#action
Future<void> setRouterByIndex(int index) async {
menuSelectedIndex = index;
String _router = routers[menuSelectedIndex]?.defaultRouter ?? " ";
if (_router != router2) {
await setRouter(_router);
}
}
#action
setArguments(Object? obj) {
arguments = obj;
}
#action
setCycleEnable(bool enable) async{
cycleEnable = enable;
log('noaction'+cycleEnable.toString());
}
#action
setCycleStarted(bool started) {
isCycleStarted = started;
}
#action
setMenuSelectedIndex(int index) {
menuSelectedIndex = index;
}
#action
Widget getByIndex(int index) {
return routers[index]?.page ?? const SizedBox.shrink();
}
#action
Future<Widget> getPage(String router, int pageIdentify) async {
if (pageIdentify != menuSelectedIndex) {
return getByIndex(pageIdentify);
}
for (var item in routers.values) {
if (item.routers.any((r) => r == router) ||
(router == "" && item.defaultRouter == pathHome)) {
item.page = _createPageByRouter(router);
return _createPageByRouter(router);
// TODO: strange behaviour where returning item.page is identified as Widget?
}
}
return const SizedBox.shrink();
}
#action
Widget _createPageByRouter(String router) {
bool isTablet = DeviceType.get().isTablet!;
Widget page = const SizedBox.shrink();
bool isShowSubMenu = true;
log('router'+router);
switch (router) {
case '':
case pathHome:
page = isTablet
? const HomeTabletPage(title: "Home")
: const HomePhonePage(title: "Home");
if (isTablet) isShowSubMenu = false;
break;
case pathMessageReceived:
page = isTablet
? MessageListTabletPage(type: 1)
: MessageListPhonePage(type: 1);
break;
case pathMessageSend:
page = isTablet
? MessageListTabletPage(type: 0)
: MessageListPhonePage(type: 0);
break;
case pathInformation:
page = isTablet ? const InformationTabletPage() : const InformationPhonePage();
break;
case pathExternalDevice:
page =
isTablet ? const ExternalDeviceTabletPage() : const ExternalDevicePhonePage();
break;
case pathErrorCode:
page = isTablet ? const ErrorCodeTabletPage() : const ErrorCodePhonePage();
break;
case pathSetting:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: true)
: SettingPhonePage(title: "Configurações", defaultPage: true);
break;
case pathSettingUnitConfig:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: false)
: SettingPhonePage(title: "Configurações", defaultPage: false);
break;
case pathOperationCycle:
if (isTablet) {
return const OperationCycleMasterTablet();
} else {
if (isCycleStarted) {
page = const OperationCyclePhonePage();
} else {
page = const OperationCycleHomePhonePage();
}
}
if (isTablet) isShowSubMenu = false;
break;
default:
page = const SizedBox.shrink();
break;
}
if (isShowSubMenu && isTablet) {
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
IoC.get<SubMenu>()!,
Expanded(
child: page,
),
],
);
}
return page;
}
#action
Future initPages() async {
initRoutes();
pages = [];
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
setMenuSelectedIndex(0);
setRouterByHomePage(pathHome);
}
final page = await getPage(router2, key as int);
pages.add(page);
log('initPages'+ pages.toString());
}
}
Future<bool> _treatAuth() async {
final authStatusService = IoC.get<AuthStatusService>()!;
final authStatus = await authStatusService.syncData();
if (authStatus != null && authStatus.requireLogin) {
pathPage = pathAuthentication;
router2 = pathAuthentication;
IoC.get<NavigationService>()!.navigateWithoutHistory(pathAuthentication);
return true;
}
return false;
}
#action
Future initRoutes() async {
if (DeviceType.get().isPhone!) {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathInformation, pathExternalDevice], null),
2: PageWithRouter([pathErrorCode], null),
3: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable) {
routers.addAll({
4: PageWithRouter([pathOperationCycle], null),
5: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
} else {
routers.addAll({
4: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
}
} else {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathMessageReceived, pathMessageSend], null),
2: PageWithRouter([pathInformation, pathExternalDevice], null),
3: PageWithRouter([pathErrorCode], null),
4: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable == true) {
routers.addAll({
5: PageWithRouter([pathOperationCycle], null)
});
log('initRoutes'+routers.toString());
}
}
}
#action
Future refreshCycleBtn() async {
tabs = [];
if (DeviceType.get().isPhone!) {
tabs = List.from(tabs..addAll(await getTabsPhone(menuSelectedIndex)));
} else {
tabs = List.from(tabs..addAll(await getTabs(menuSelectedIndex)));
log('tabs'+tabs.toString());
}
await initRoutes();
await _verifyOperationCycleEnabled();
if (cycleEnable == false) {
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
pageController!.jumpToPage(0);
} else {
await initPages();
}
} else {
await initPages();
pages = [];
List<Widget> newPages = [];
for (final key in routers.keys) {
final page = await getPage(router2, key as int);
newPages.add(page);
pages = List.from(pages..addAll(newPages));
}
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
} else {
await initRoutes();
await initPages();
}
}
}
Future<List<Widget>> getTabs(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle && cycleEnable == false) {
continue;
}
newTabs.add(
SizedBox(
height: 70,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
shape: const ContinuousRectangleBorder(
borderRadius: BorderRadius.all(Radius.zero),
),
),
child: _getTabIcon(routers[key]!.defaultRouter),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
log('newtabs'+newTabs.toString());
return newTabs;
}
Widget _getTabIcon(String router) { // os cases com icons
switch (router) {
case '': //setado para home
return const Icon(
Icons.home,
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathHome:
return const Icon(
Icons.home,
key: Key('btn_home_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathMessageReceived:
var icon = const Icon(
Icons.mail,
key: Key('btn_message_page'),
color: Colors.white,
size: 30,
);
return Observer(builder: (_) {
if (_menuIconsController.countMessages == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countMessages.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathInformation:
return const Icon(
Icons.info,
key: Key('btn_information_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathErrorCode:
var icon = const Icon(
Icons.warning,
key: Key('btn_erro_code_page'),
color: Colors.white,
);
return Observer(builder: (_) {
if (_menuIconsController.countUccErrors == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countUccErrors.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathSetting:
var icon = const Icon(
Icons.settings,
key: Key('btn_setting_page'),
color: Colors.white,
size: 30,
);
if (!_menuIconsController.showBadgetSetting!) return icon;
return Observer(builder: (_) {
return Badge(
badgeContent: const Padding(
padding: EdgeInsets.all(2.0),
child: Text(
"!",
style: TextStyle(
color: Colors.white,
),
),
),
child: icon,
);
});
case NavigationControllerBase.pathOperationCycle:
return const Icon(
MtfIcons.operationalCycleHomeMenu,
key: Key('btn_operation_cycle'),
color: Colors.white,
size: 30,
);
default:
return const SizedBox.shrink();
}
}
Future<List<Widget>> getTabsPhone(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
continue;
}
newTabs.add(
Expanded(
child: TextButton(
style: TextButton.styleFrom(
primary: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
child: _getTabIcon(routers[key]!.defaultRouter),
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
return newTabs;
}
Future setListenerPage() async {
// TODO: refactor this lambda;
var listener = () async { // ignore: prefer_function_declarations_over_variables
if (DeviceType.get().isPhone!) {
await getTabsPhone(menuSelectedIndex);
} else {
await getTabs(menuSelectedIndex);
}
double page = pageController!.page!;
await setRouterByIndex(page.round());
};
pageController!.removeListener(listener);
pageController!.addListener(listener);
}
void setMenuSelectedByPath(String? path) { //init menuSeletecteIndex =id
routers.forEach((id, page) {
if (page.routers.any((r) => r == path)) {
menuSelectedIndex = id as int;
}
});
}
Future _verifyOperationCycleEnabled() async { //setCycleEnable true || false
final _operationCycleService = IoC.get<OperationCycleService>()!;
final operationCycle = await _operationCycleService.syncData();
if (operationCycle == null) {
setCycleEnable(false);
} else {
setCycleEnable(true);
}
}
}
class PageWithRouter {
List<String> routers;
Widget? page;
String get defaultRouter => routers.first;
PageWithRouter(this.routers, this.page);
}
I have already tried changing WidgetsBinding to Future.delay and tried initiating different ways inside refreshCycleBtn().

Read data from Flutter via Bluetooth

I am trying to get the data from my bluetooth device. My problem is with the Flutter code to get such data.
services/sensor.dart
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:minertti/main.dart';
class SensorPage extends StatefulWidget {
const SensorPage({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
#override
_SensorPageState createState() => _SensorPageState();
}
class _SensorPageState extends State<SensorPage> {
String service_uuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
String charaCteristic_uuid = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
late bool isReady;
late Stream<List<int>> stream;
late List _temphumidata;
double _charge = 0;
double _data_1 = 0;
double _data_2 = 0;
#override
void initState() {
super.initState();
super.initState();
isReady = false;
connectToDevice();
}
void dispose() {
widget.device.disconnect();
super.dispose();
}
connectToDevice() async {
if (widget.device == null) {
_pop();
return;
}
new Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
_pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
_pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
if (widget.device == null) {
_pop();
return;
}
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString().isNotEmpty) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString().isNotEmpty) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
setState(() {
isReady = true;
});
}
});
}
});
if (!isReady) {
_pop();
}
}
_pop() {
Navigator.of(context).pop(true);
}
String _dataParser(List<int> dataFromDevice) {
return utf8.decode(dataFromDevice);
}
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text('dht11 Sensor'),
// ),
body: Container(
child: !isReady
? Center(
child: Text(
"Waiting...",
style: TextStyle(
fontSize: 24, color: Color.fromARGB(255, 0, 0, 0)),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
if (snapshot.connectionState == ConnectionState.active) {
var data = snapshot.data as List<int>;
var currentValue = _dataParser(data);
print("REALDATA: $data");
_temphumidata = currentValue.split(",");
//_charge = double.parse('${_temphumidata[0]}');
//_data_1 = double.parse('${_temphumidata[1]}');
//_data_2 = _temphumidata[2];
return DeviceScreen1(
device: widget.device,
//charge: _charge,
//data_2: _data_2,
//data_1: _data_1,
charge: 90,
data_1: "Data 1",
data_2: "Data 2");
} else {
return Text('Check the stream');
}
},
),
)),
);
}
}
var data = snapshot.data as List;
var currentValue = _dataParser(data);
They do not show values. But, from my Arduino I know that it does send/notify data. That is, my problem is with reading and obtaining said data.

How to download attachment in Flutter using enough_mail

I need to download the attachment from the mime message object. Bellow, I have added my class file. Which came with the mime message. Need help to extract the attachments and download them.
class EmailScreen extends StatefulWidget {
EmailScreen({
Key key,
this.mimeMessage,
this.userInfo
}) : super(key: key);
final MimeMessage mimeMessage;
final UserInfo userInfo;
#override
EmailScreenState createState() => EmailScreenState(
mimeMessage: mimeMessage,
userInfo: userInfo
);
}
class EmailScreenState extends State<EmailScreen> {
MimeMessage mimeMessage;
UserInfo userInfo;
EmailScreenState({Key key,this.mimeMessage,this.userInfo});
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}
Here is the below code that I have used to download attachments using enough_mail 1.3.6.
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: mimeMessage.findContentInfo().length,
itemBuilder: (context, index) {
ContentInfo contentInfo = mimeMessage
.findContentInfo()[index];
return Container(
padding: EdgeInsets.only(left: 10.0),
margin: EdgeInsets.only(right: 5.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
child: Text(
contentInfo.fileName,
overflow: TextOverflow.ellipsis,
)
),
IconButton(
onPressed: (){
setState(() {
MimePart mimePart = mimeMessage.getPart(contentInfo.fetchId);
Uint8List uint8List = mimePart.decodeContentBinary();
MySnackBar.show(
context,
MySnackBar.loadingIcon,
"Downloading....!"
);
saveFile(context,uint8List,contentInfo.fileName).then((value){
MySnackBar.hide(context);
if(value){
MySnackBar.show(
context,
MySnackBar.successIcon,
"Completed!"
);
}else{
MySnackBar.show(
context,
MySnackBar.errorIcon,
"Something went wrong!"
);
}
}).catchError((err){
MySnackBar.hide(context);
MySnackBar.show(
context,
MySnackBar.errorIcon,
"Something went wrong!"
);
});
});
},
icon: Icon(
Icons.download,
color: Colors.grey,
)
)
],
),
);
}
)
Future<bool> saveFile(BuildContext context,Uint8List uint8List, String fileName) async {
Directory directory;
try {
if (Platform.isAndroid) {
if (await requestPermission(Permission.storage)) {
directory = await getExternalStorageDirectory();
String newPath = "";
print(directory);
List<String> paths = directory.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
} else {
break;
}
}
newPath = newPath + "/NetxMail";
directory = Directory(newPath);
} else {
return false;
}
} else {
if (await requestPermission(Permission.photos)) {
directory = await getTemporaryDirectory();
} else {
return false;
}
}
if (!await directory.exists()) {
await directory.create(recursive: true);
}
if (await directory.exists()) {
File file = new File('${directory.path}/$fileName');
print("file path = ${file.path}");
await file.writeAsBytes(uint8List);
return true;
}
return false;
} catch (e) {
print(e);
return false;
}
}
Future<bool> requestPermission(Permission permission) async {
if (await permission.isGranted) {
return true;
} else {
var result = await permission.request();
if (result == PermissionStatus.granted) {
return true;
}
}
return false;
}

Timeline Posts are not being displayed

My timeline page is not displaying any posts of users which I'm following. The posts are working fine on the user's profile page but not showing up on timeline. Here is the code of my timeline, and I don't see any debug errors too, so how to identify what's wrong here? Did I miss something? However, for the new user sign up, it does show users to follow on the timeline page.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/pages/search.dart';
import 'package:fluttershare/widgets/header.dart';
import 'package:fluttershare/widgets/post.dart';
import 'package:fluttershare/widgets/progress.dart';
class Timeline extends StatefulWidget {
final User currentUser;
Timeline({this.currentUser});
#override
_TimelineState createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
List<Post> posts;
List<String> followingList = [];
#override
void initState() {
super.initState();
getTimeline();
getFollowing();
}
getTimeline() async {
QuerySnapshot snapshot = await timelineRef
.document(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.getDocuments();
List<Post> posts =
snapshot.documents.map((doc) => Post.fromDocument(doc)).toList();
setState(() {
this.posts = posts;
});
}
getFollowing() async {
QuerySnapshot snapshot = await followingRef
.document(currentUser.id)
.collection('userFollowing')
.getDocuments();
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList();
});
}
buildTimeline() {
if (posts == null) {
return circularProgress();
} else if (posts.isEmpty) {
return buildUsersToFollow();
} else {
return ListView(children: posts);
}
}
buildUsersToFollow() {
return StreamBuilder(
stream:
usersRef.orderBy('timestamp', descending: true).limit(30).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<UserResult> userResults = [];
snapshot.data.documents.forEach((doc) {
User user = User.fromDocument(doc);
final bool isAuthUser = currentUser.id == user.id;
final bool isFollowingUser = followingList.contains(user.id);
if (isAuthUser) {
return;
} else if (isFollowingUser) {
return;
} else {
UserResult userResult = UserResult(user);
userResults.add(userResult);
}
});
return Container(
color: Theme.of(context).accentColor.withOpacity(0.2),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.person_add,
color: Theme.of(context).primaryColor,
size: 30.0,
),
SizedBox(
width: 8.0,
),
Text(
"Users to Follow",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 30.0,
),
),
],
),
),
Column(children: userResults),
],
),
);
},
);
}
#override
Widget build(context) {
return Scaffold(
appBar: header(context, isAppTitle: true),
body: RefreshIndicator(
onRefresh: () => getTimeline(),
child: buildTimeline(),
),
);
}
}
Update your code
buildTimeline() {
if (posts == null) {
return circularProgress();
} else if (posts.isEmpty) {
return buildUsersToFollow();
} else {
return ListView.builder(
itemCount: posts.length,
itemBuilder: (BuildContext ctxt, int index) {
return Text(posts[index].toString);
});
}
}
ListView takes Widgets as a children. posts is not a widgets of any kind.