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;
}
Related
I am developing a voice/video-calling app using Flutter and Agora rtc engine (v 5.3.1). I have followed all the necessary steps for generating token and joining a channel. But, I can't see the local camera view in the UI and the logs gives me warning saying: "onWarning warn 8 msg invalid view for local video". Any leads would be big help, thanks in advance.
Logs:
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 8 msg invalid view for local video
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
I/spdlog (30579): [2023-01-04 21:02:33.375] [0] [info] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:72 onJoinChannelSuccess
channel b8667da0-8c6a-11ed-a9fb-578e8ad35bd6 uid 1
W/spdlog (30579): [2023-01-04 21:02:33.377] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
Call Screen Widget:
imports...
String baseUrl = 'https://...';
class CallScreen extends ConsumerStatefulWidget {
final Call call;
const CallScreen({Key? key, required this.call, }) : super(key: key);
#override
_CallScreenState createState() => _CallScreenState();
}
class _CallScreenState extends ConsumerState<CallScreen> {
int uid = 1;
List<int> _remoteUids = [];
bool isJoined = false,
switchCamera = true,
openMicrophone = true,
enableSpeakerphone = false;
late bool openCamera;
late RtcEngine _engine;
#override
void initState() {
initAgora();
openCamera = widget.call.isVideoCall;
super.initState();
}
Future<String?> _getToken() async {
String url = baseUrl + "/rtc/" + widget.call.callId + "/publisher/uid/" + uid.toString() + "/";;
String? token;
try{
var resp = await http.get(Uri.parse(url));
if(resp.statusCode == 200){
token = jsonDecode(resp.body)['rtcToken'];
return token;
}
else{
showMySnackBar(context: context, content: "Token Status ERR: "+jsonDecode(resp.body)['message']);
return null;
}
}
catch(e){
showMySnackBar(context: context, content: "Token Err: "+e.toString());
return null;
}
}
void _joinChannel() async {
String? token = await _getToken();
if(token != null){
await _engine.joinChannel(token, widget.call.callId, null, uid);
}
else{
showMySnackBar(context: context, content: 'Token is null!');
}
}
void _leaveChannel() async {
ref.read(callControllerProvider).endCall(
widget.call.callerId,
widget.call.receiverId,
context,
widget.call.isGroupChat
);
if(widget.call.isVideoCall) await _engine.stopPreview();
await _engine.leaveChannel();
}
void _switchCamera() {
_engine.switchCamera()
.then((value) {
setState(() {
switchCamera = !switchCamera;
});
})
.catchError((err) {
//print('switchCamera $err');
});
}
void _switchMicrophone() async {
// await _engine.muteLocalAudioStream(!openMicrophone);
await _engine.enableLocalAudio(!openMicrophone)
.then((value) {
setState(() {
openMicrophone = !openMicrophone;
});
})
.catchError((err) {
// print('enableLocalAudio $err');
});
}
void _switchSpeakerphone() {
_engine.setEnableSpeakerphone(!enableSpeakerphone)
.then((value) {
setState(() {
enableSpeakerphone = !enableSpeakerphone;
});
})
.catchError((err) {
//print('setEnableSpeakerphone $err');
});
}
void initAgora() async {
try{
await [Permission.microphone, Permission.camera].request();
_engine = await RtcEngine.createWithContext(RtcEngineContext(AgoraConfig.appId));
_engine.setEventHandler(
RtcEngineEventHandler(
warning: (warn) {
showMySnackBar(context: context, content: "Warn: "+warn.name);
},
error: (err) {
showMySnackBar(context: context, content: 'OnErr event: '+err.name);
},
joinChannelSuccess: (String channel, int userId, int elapsed) {
// print("local user ${connection.localUid} joined");
if(mounted){
setState(() {
isJoined = true;
uid = userId;
});
}
showMySnackBar(context: context, content: 'You ($userId) have joined the call!');
},
userJoined: (int remoteUid, int elapsed) {
//debugPrint("remote user $remoteUid joined");
if(mounted){
setState(() {
_remoteUids.add(remoteUid);
});
}
},
userOffline: (int remoteUid, UserOfflineReason reason) {
//debugPrint("remote user $remoteUid left channel");
if(mounted){
setState(() {
_remoteUids.removeWhere((element) => element == remoteUid);
});
}
},
leaveChannel: (stats) {
if(mounted){
setState(() {
isJoined = false;
if(!widget.call.isGroupChat || _remoteUids.length == 1){
_remoteUids.clear();
}
});
}
},
// onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
// debugPrint('[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token');
// },
),
);
await _engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
//await _engine.setClientRole(ClientRole.Broadcaster);
await _engine.enableVideo();
if(widget.call.isVideoCall){
await _engine.startPreview();
}
else{
await _engine.muteLocalVideoStream(true);
await _engine.muteAllRemoteVideoStreams(true);
}
_joinChannel();
}
catch(e){
showMySnackBar(context: context, content: "Init Err: "+e.toString());
}
}
#override
void dispose() {
_leaveChannel();
_engine.destroy();
super.dispose();
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUids.isNotEmpty) {
//TODO check for video on or off or if video call:
return rtc_remote_view.SurfaceView(
uid: _remoteUids[0],
channelId: widget.call.callId,
);
}
else {
return const Text(
'Please wait for others to join',
textAlign: TextAlign.center,
);
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async { _leaveChannel(); return true; },
child: Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 18.0, bottom: 12),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: ColoredBox(
color: Colors.grey.shade200,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 125, maxHeight: 175),
child: AspectRatio(
aspectRatio: 9/16,
child: Center(
child: isJoined
?
( //TODO: check for video on or off or if video call:
openCamera
? rtc_local_view.SurfaceView(
channelId: widget.call.callId,
)
: const Icon(
Icons.videocam_off_rounded,
color: Colors.black,
size: appActionsIconsSize,
)
)
: const CircularProgressIndicator(),
),
),
),
),
),
),
),
],
),
),
);
}
}
I found the issue: I was not setting the ClientRoleType correctly and that caused error in finding local view. One needs to define ClientRoleType (based on your logic) and ChannelProfileType.broadcast and everything seems to work.
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().
I am tring to pas data to user profile screen from firebase
i have got error Unhandled Exception: 'package:cloud_firestore/src/collection_reference.dart': Failed assertion: line 116 pos 14: 'path.isNotEmpty': a document path must be a non-empty string
what do i need fix?
user prfile controller
lass ProfileController extends GetxController {
final Rx<Map<String, dynamic>> _user = Rx<Map<String, dynamic>>({});
Map<String, dynamic> get user => _user.value;
Rx<String> _uid = ''.obs;
updateUserID(String uid) {
_uid.value = uid;
getUserData();
}
void getUserData() async {
print("HELLO");
List<String> thumbnails = [];
// var myVideos = await firestore
// .collection('videos')
// .where('uid', isEqualTo: "9GFyxV41rkd0Iu0oy1tctobVNk92")
// .get();
//
// for (int i = 0; i < myVideos.docs.length; i++) {
// thumbnails.add((myVideos.docs[i].data() as dynamic)['thumbnail']);
// }
DocumentSnapshot userDoc =
await firestore.collection('users').doc(_uid.value).get();
final userData = userDoc.data()! as dynamic;
print(userData);
String name = userData['name'];
String profilePhoto = userData['profilePhoto'];
int likes = 0;
int followers = 0;
int following = 0;
bool isFollowing = false;
// for (var item in myVideos.docs) {
// likes += (item.data()['likes'] as List).length;
// }
// var followerDoc = await firestore
// .collection('users')
// .doc(_uid.value)
// .collection('followers')
// .get();
// var followingDoc = await firestore
// .collection('users')
// .doc(_uid.value)
// .collection('following')
// .get();
// followers = followerDoc.docs.length;
// following = followingDoc.docs.length;
// firestore
// .collection('users')
// .doc(_uid.value)
// .collection('followers')
// .doc(authController.user.uid)
// .get()
// .then(
// (value) {
// if (value.exists) {
// isFollowing = true;
// } else {
// isFollowing = false;
// }
// },
// );
_user.value = {
'followers': followers.toString(),
'following': following.toString(),
'isFollowing': isFollowing,
'likes': likes.toString(),
'profilePhoto': profilePhoto,
'name': name,
'thumbnails': thumbnails,
};
update();
}
followUser() async {
var doc = await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.get();
if (!doc.exists) {
await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.set({});
await firestore
.collection('users')
.doc(authController.user.uid)
.collection('following')
.doc(_uid.value)
.set({});
_user.value
.update('followers', (value) => (int.parse(value) + 1).toString());
} else {
await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.delete();
await firestore
.collection('users')
.doc(authController.user.uid)
.collection('following')
.doc(_uid.value)
.delete();
_user.value
.update('followers', (value) => (int.parse(value) - 1).toString());
}
_user.value.update('isFollowing', (value) => !value);
update();
}
}
auth Controller
class AuthController extends GetxController {
static AuthController instance = Get.find(); //TODO 2
var displayName = '';
var displayPhoto = '';
String myId = '';
late Rx<User?> _user; //TODO 1
User get user => _user.value!; //TODO 3
Rx<File?>? pickedImageVal;
File? get profilePhoto => pickedImageVal!.value;
FirebaseAuth auth = FirebaseAuth.instance;
User? get userProfile => auth.currentUser;
var isSignedIn = false.obs;
var _googleSignIn = GoogleSignIn();
var googleAccount = Rx<GoogleSignInAccount?>(null);
var firebaseStorage = FirebaseStorage.instance;
var firestore = FirebaseFirestore.instance;
// var authController = AuthController.instance;
#override
void onReady() {
super.onReady();
_user = Rx<User?>(firebaseAuth.currentUser);
_user.bindStream(firebaseAuth.authStateChanges());
ever(_user, _setInitialScreen);
}
_setInitialScreen(User? user) {
if (user == null) {
Get.offAll(OnboardingPage());
} else {
Get.offAll(const Home());
}
}
#override
void onInit() {
displayName = userProfile != null ? userProfile!.displayName! : '';
super.onInit();
}
void pickImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
Get.snackbar(
'プロファイル写真', 'You have successfully selected your profile picture!');
}
pickedImageVal = Rx<File?>(File(pickedImage!.path));
}
Future<String> _uploadToStorage(File image) async {
Reference ref =
firebaseStorage.ref().child('profilePics').child(auth.currentUser!.uid);
UploadTask uploadTask = ref.putFile(image);
TaskSnapshot snap = await uploadTask;
String downloadUrl = await snap.ref.getDownloadURL();
return downloadUrl;
}
void signUp(String name, String email, String password, File? image) async {
try {
await auth
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) {
displayName = name;
auth.currentUser!.updateDisplayName(name);
});
isSignedIn = true.obs;
String downloadUrl = await _uploadToStorage(profilePhoto!);
model.UserM user = model.UserM(
name: name,
email: email,
uid: auth.currentUser!.uid,
profilePhoto: downloadUrl,
);
await firestore
.collection('users')
.doc(auth.currentUser!.uid)
.set(user.toJson());
update();
Get.offAll(() => Root());
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'weak-password') {
message = 'The password provided is too weak.';
} else if (e.code == 'email-already-in-use') {
message = ('The account already exists for that email.');
} else {
message = e.message.toString();
}
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signIn(String email, String password) async {
try {
await auth
.signInWithEmailAndPassword(email: email, password: password)
.then((value) => displayName = userProfile!.displayName!);
isSignedIn = true.obs;
myId = userProfile!.uid; //TODO 4
update();
Get.offAll(() => Root());
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'wrong-password') {
message = 'Invalid Password. Please try again!';
} else if (e.code == 'user-not-found') {
message =
('The account does not exists for $email. Create your account by signing up.');
} else {
message = e.message.toString();
}
Get.snackbar('ユーザー名が存在しません。', 'アカウントを作成してください。',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
//TODO; what is Get.snackbar.e.tostring(), means?
Get.snackbar(
'Error occured!',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white,
);
}
}
void resetPassword(String email) async {
try {
await auth.sendPasswordResetEmail(email: email);
Get.back();
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'user-not-found') {
message =
('The account does not exists for $email. Create your account by signing up.');
} else {
message = e.message.toString();
}
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signInWithGoogle() async {
try {
googleAccount.value = await _googleSignIn.signIn();
print(googleAccount.value);
displayName = googleAccount.value!.displayName!;
displayPhoto = googleAccount.value!.photoUrl!;
isSignedIn.value = true;
update(); // <-- without this the isSignedin value is not updated.
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signout() async {
try {
await auth.signOut();
await _googleSignIn.signOut();
displayName = '';
isSignedIn.value = false;
update();
Get.offAll(() => Root());
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
}
//
// extension StringExtention on String{
// String capitalizedString(){
// return'${this[0].toUpperCase()}${this.substring(1)}';
// }
// }
user profile screen
class ProfileScreen extends StatefulWidget {
final String uid;
ProfileScreen({Key? key, required this.uid}) : super(key: key);
#override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
final ProfileController profileController = Get.put(ProfileController());
#override
void initState() {
super.initState();
profileController.updateUserID(widget.uid);
}
#override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(
init: ProfileController(),
builder: (controller) {
// if (controller.user.isEmpty) {
// return Center(
// child: CircularProgressIndicator(),
// );
// }
print(controller.user);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black12,
leading: Icon(Icons.person_add_alt_1_outlined),
actions: [
Icon(Icons.more_horiz),
],
title: Text(controller.user['name'].toString()),
),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipOval(
child: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: controller.user['profilePhoto']
.toString(),
height: 100,
width: 100,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error)),
)
],
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
Text(controller.user['following'].toString()),
SizedBox(
height: 5,
),
Text('following'),
],
),
Container(
color: Colors.black54,
width: 1,
height: 15,
margin: EdgeInsets.symmetric(horizontal: 15),
),
Column(
children: [
Text(controller.user['followers'].toString()),
SizedBox(
height: 5,
),
Text('follower'),
],
),
Container(
color: Colors.black54,
width: 1,
height: 15,
margin: EdgeInsets.symmetric(horizontal: 15),
),
Column(
children: [
Text(controller.user['likes'].toString()),
SizedBox(
height: 5,
),
Text('likes'),
],
)
],
),
SizedBox(
height: 15,
),
Container(
width: 140,
height: 47,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black12,
),
),
child: Center(
child: InkWell(
onTap: () {
// if (widget.uid == authController.user.uid) {
// authController.signOut();
// } else {
// controller.followUser();
},
// },
child: const Text(
"",
// widget.uid == authController.user.uid
// ? 'sign out'
// controller.user['isFollowing']
// ? 'unfollow'
// : 'follow',
),
),
),
),
I am Currently trying to download a file from URL using Dio Package. I can successfully download the file but I want to display the progress in an Alert Dialog and once Download is Successful Then it Should Display "Download Successful". I tried but couldn't find any solution
Packages I used are : Dio, Path_Provider and permission_handler.
my Code :
class WatchView extends StatefulWidget {
#override
_WatchViewState createState() => _WatchViewState();
}
class _WatchViewState extends State<WatchView> {
final Dio dio = Dio();
bool loading = false;
double progress = 0;
Future<bool> saveVideo(String url, String fileName) async {
Directory directory;
try {
if (Platform.isAndroid) {
if (await _requestPermission(Permission.storage)) {
String newPath =
"storage/emulated/0/Android/data/com.appname.test./files/";
print(directory);
newPath = newPath + "folder";
directory = Directory(newPath);
} else {
return false;
}
} else {
if (await _requestPermission(Permission.photos)) {
directory = await getTemporaryDirectory();
} else {
return false;
}
}
File saveFile = File(directory.path + "/$fileName");
if (!await directory.exists()) {
await directory.create(recursive: true);
}
if (await directory.exists()) {
await dio.download(url, saveFile.path,
onReceiveProgress: (value1, value2) {
setState(() {
progress = value1 / value2;
});
});
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;
}
downloadFile(String file1) async {
setState(() {
loading = true;
progress = 0;
});
bool downloaded = await saveVideo(file1, "test.file_extention");
if (downloaded) {
print("File Downloaded");
} else {
print("Problem Downloading File");
}
setState(() {
loading = false;
});
}
#override
Widget build(BuildContext context) {
var provider = Provider.of<ProviderModel>(context);
return Scaffold(
appBar: AppBar(
elevation: 0.0,
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
),
body: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Container(
),
floatingActionButton: FloatingActionButton.extended(
label: Text(
'Download',
),
backgroundColor: Colors.indigo[700],
icon: Icon(Icons.download_rounded),
onPressed: () {
downloadFile(
"Download URL,
);
},
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
);
},
);
}
}
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.