I just started to learn flutter. I wrote an api with Nodejs and deployed to heroku.
I tested the api by Postman and browser, it works fine with strings and json and pdf file.
How can I make flutter to download pdf file from api
My nodejs api
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/getPdfFile/:Name', function(req, res, next) {
var path = require('path');
console.log(__dirname);
var file = path.join(__dirname, '../pdfs/'+req.params.Name+'.pdf');
res.download(file, function (err) {
if (err) {
console.log("Error");
console.log(err);
} else {
console.log("Success");
}
});
});
router.get('/getName', function(req, res, next) {
res.json({"foo": "bar"});
});
router.get('/getNamee', function(req, res, next) {
res.send('some string');
});
My flutter main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(new MaterialApp(
home: new HomePage(),
));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> {
List data;
//apidenemee4.herokuapp.com/getPdfFile/SpaceX
Future getData() async {
var response = await http.get(
Uri.encodeFull("https://mynodeapi4.herokuapp.com//getPdfFile/SpaceX"),
headers: {"Accept": "application/json"});
print(response.body);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new RaisedButton(
child: new Text("Get data"),
onPressed: getData,
),
),
);
}
}
You can use flutter_downloader - to download and open the file, path_provider- to access device paths and permission_handler - to handle the device storage permissions.
Please add following permissions in AndroidManifest.xml for Android devices
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Add the provider inside application tag
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.snj07.flutter_app_stw.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
Create a folder with xml name inside src/main and add the following content in provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
Please customize the following example to download the PDF file and open it in your project. It's taken from flutter_downloader example. I also updated some code for permission handling as per the latest plugin.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
const debug = true;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(debug: debug);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(
title: 'Downloader',
platform: platform,
),
);
}
}
class MyHomePage extends StatefulWidget with WidgetsBindingObserver {
final TargetPlatform platform;
MyHomePage({Key key, this.title, this.platform}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _documents = [
{
'name': 'PDF',
'link':
'http://darwinlogic.com/uploads/education/iOS_Programming_Guide.pdf'
},
];
List<_TaskInfo> _tasks;
List<_ItemHolder> _items;
bool _isLoading;
bool _permissionReady;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState() {
super.initState();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
_isLoading = true;
_permissionReady = false;
_prepare();
}
#override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
void _bindBackgroundIsolate() {
bool isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) {
if (debug) {
print('UI Isolate Callback: $data');
}
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
final task = _tasks?.firstWhere((task) => task.taskId == id);
if (task != null) {
setState(() {
task.status = status;
task.progress = progress;
});
}
});
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
static void downloadCallback(
String id, DownloadTaskStatus status, int progress) {
if (debug) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
}
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Builder(
builder: (context) => _isLoading
? new Center(
child: new CircularProgressIndicator(),
)
: _permissionReady
? new Container(
child: new ListView(
padding: const EdgeInsets.symmetric(vertical: 16.0),
children: _items
.map((item) => item.task == null
? new Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: Text(
item.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 18.0),
),
)
: new Container(
padding: const EdgeInsets.only(
left: 16.0, right: 8.0),
child: InkWell(
onTap: item.task.status ==
DownloadTaskStatus.complete
? () {
_openDownloadedFile(item.task)
.then((success) {
if (!success) {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text(
'Cannot open this file')));
}
});
}
: null,
child: new Stack(
children: <Widget>[
new Container(
width: double.infinity,
height: 64.0,
child: new Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new Text(
item.name,
maxLines: 1,
softWrap: true,
overflow:
TextOverflow.ellipsis,
),
),
new Padding(
padding:
const EdgeInsets.only(
left: 8.0),
child: _buildActionForTask(
item.task),
),
],
),
),
item.task.status ==
DownloadTaskStatus
.running ||
item.task.status ==
DownloadTaskStatus.paused
? new Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child:
new LinearProgressIndicator(
value: item.task.progress /
100,
),
)
: new Container()
]
.where((child) => child != null)
.toList(),
),
),
))
.toList(),
),
)
: new Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
'Please grant accessing storage permission to continue -_-',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blueGrey, fontSize: 18.0),
),
),
SizedBox(
height: 32.0,
),
FlatButton(
onPressed: () {
_checkPermission().then((hasGranted) {
setState(() {
_permissionReady = hasGranted;
});
});
},
child: Text(
'Retry',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 20.0),
))
],
),
),
)),
);
}
Widget _buildActionForTask(_TaskInfo task) {
if (task.status == DownloadTaskStatus.undefined) {
return new RawMaterialButton(
onPressed: () {
_requestDownload(task);
},
child: new Icon(Icons.file_download),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.running) {
return new RawMaterialButton(
onPressed: () {
_pauseDownload(task);
},
child: new Icon(
Icons.pause,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.paused) {
return new RawMaterialButton(
onPressed: () {
_resumeDownload(task);
},
child: new Icon(
Icons.play_arrow,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.complete) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text(
'Open',
style: new TextStyle(color: Colors.green),
),
RawMaterialButton(
onPressed: () {
_delete(task);
},
child: Icon(
Icons.delete_forever,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else if (task.status == DownloadTaskStatus.canceled) {
return new Text('Canceled', style: new TextStyle(color: Colors.red));
} else if (task.status == DownloadTaskStatus.failed) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text('Failed', style: new TextStyle(color: Colors.red)),
RawMaterialButton(
onPressed: () {
_retryDownload(task);
},
child: Icon(
Icons.refresh,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else {
return null;
}
}
void _requestDownload(_TaskInfo task) async {
task.taskId = await FlutterDownloader.enqueue(
url: task.link,
headers: {"auth": "test_for_sql_encoding"},
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true);
}
void _cancelDownload(_TaskInfo task) async {
await FlutterDownloader.cancel(taskId: task.taskId);
}
void _pauseDownload(_TaskInfo task) async {
await FlutterDownloader.pause(taskId: task.taskId);
}
void _resumeDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
task.taskId = newTaskId;
}
void _retryDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
task.taskId = newTaskId;
}
Future<bool> _openDownloadedFile(_TaskInfo task) {
return FlutterDownloader.open(taskId: task.taskId);
}
void _delete(_TaskInfo task) async {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await _prepare();
setState(() {});
}
Future<bool> _checkPermission() async {
if (widget.platform == TargetPlatform.android) {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
if (statuses[Permission.storage] == PermissionStatus.denied) {
if (await Permission.contacts.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
Future<Null> _prepare() async {
final tasks = await FlutterDownloader.loadTasks();
int count = 0;
_tasks = [];
_items = [];
_tasks.addAll(_documents.map((document) =>
_TaskInfo(name: document['name'], link: document['link'])));
_items.add(_ItemHolder(name: 'Documents'));
for (int i = count; i < _tasks.length; i++) {
_items.add(_ItemHolder(name: _tasks[i].name, task: _tasks[i]));
count++;
}
tasks?.forEach((task) {
for (_TaskInfo info in _tasks) {
if (info.link == task.url) {
info.taskId = task.taskId;
info.status = task.status;
info.progress = task.progress;
}
}
});
_permissionReady = await _checkPermission();
_localPath = (await _findLocalPath()) + Platform.pathSeparator + 'Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
setState(() {
_isLoading = false;
});
}
Future<String> _findLocalPath() async {
final directory = widget.platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
}
class _TaskInfo {
final String name;
final String link;
String taskId;
int progress = 0;
DownloadTaskStatus status = DownloadTaskStatus.undefined;
_TaskInfo({this.name, this.link});
}
class _ItemHolder {
final String name;
final _TaskInfo task;
_ItemHolder({this.name, this.task});
}
Related
`import 'package:flutter/material.dart';
import 'package:device_apps/device_apps.dart';
import 'package:app_uninstaller/app_uninstaller.dart';
import 'package:screenshot/screenshot.dart';
import 'package:share/share.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'dart:typed_data';
import 'package:audioplayers/audioplayers.dart';
class AppsListScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,),
home: Scaffold(
appBar: AppBar(
title: Text("Banned Apps In Your Mobile"),
),
body: appslist(),
backgroundColor: Colors.grey,
),
debugShowCheckedModeBanner: false,
//backgroundColor: Colors.grey,
);
}
}
class appslist extends StatefulWidget {
#override
_appslist createState() => _appslist();
}
final AudioPlayer audioPlayer=AudioPlayer();
class _appslist extends State with WidgetsBindingObserver {
String uninstallStatus = "";
bool isEmpty = false;
DateTime now=new DateTime.now();
final ScreenshotController _screenshotcontroller = ScreenshotController();
List list=[];
AudioCache audioCache=AudioCache(fixedPlayer: audioPlayer);
bool stopAudio=false;
#override
void initState() {
// TODO: implement initState
WidgetsBinding.instance.addObserver(this);
super.initState();
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
stop();
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
setState(() {});
break;
}
}
#override
Widget build(BuildContext context) {
double height=MediaQuery.of(context).size.height;
double width=MediaQuery.of(context).size.width;
return FutureBuilder <List<Application>>(
future: DeviceApps.getInstalledApplications(
includeAppIcons: true,
),
builder: (BuildContext context, AsyncSnapshot <List<Application>> data) {
if (data.data == null) {
return Center(child: CircularProgressIndicator());
} else {
List<Application> apps = data.data;
List<String> packageName = [
'in.mohalla.sharechat',
'com.tencent.mm',
'com.tencent.mobileqq',
'com.mncgames.play.kikorun',
'com.getvoo.voodriver',
'com.nimbuzz',
'app.buzz.share',
'com.qzone',
'com.viber.voip',
'jp.naver.line.android',
'com.imo.android.imoim',
'com.campmobile.snow',
'com.ss.android.ugc.trill',
'com.bsb.hike',
'com.zhiliaoapp.musically',
'video.like',
'com.getsamosa',
'com.lenovo.anyshare.gps',
'cn.xender',
'com.dewmobile.kuaiya.play',
'com.UCMobile.intl',
'com.uc.browser.en',
'com.cmcm.live',
'sg.bigo.live',
'us.zoom.videomeetings',
'com.uc.vmate',
'com.asiainno.uplive',
'com.cheerfulinc.flipagram',
'com.intsig.camscanner',
'com.commsource.beautyplus',
'com.truecaller',
'com.pubg.krmobile',
'com.nono.android',
'com.hcg.cok.gp',
'com.mobile.legends',
'club.fromfactory',
'com.alibaba.aliexpresshd',
'com.globalegrow.app.gearbest',
'com.banggood.client',
'com.miniinthebox.android',
'com.tinydeal.android',
'com.dhgate.buyer',
'com.lightinthebox.android',
'com.ericdress.application',
'com.zaful',
'com.tlz.tbdress',
'com.rosegal',
'com.zzkko',
'com.romwe',
'com.tinder',
'com.trulymadly.android.app',
'com.ftw_and_co.happn',
'com.aisle.app',
'com.coffeemeetsbagel',
'com.okcupid.okcupid',
'co.hinge.app',
'com.azarlive.android',
'com.bumble.app',
'com.p1.mobile.putong',
'de.affinitas.za.co.elitesingles.and',
'com.taggedapp',
'com.couchsurfing.mobile.android',
'com.qihoo.security',
'com.instagram.android',
'co.ello.ElloApp',
'com.snapchat.android',
'com.eterno',
'com.newsdog',
'com.pratilipi.mobile.android',
'com.luxeva.popxo',
'com.capcipcup.mommp3',
'com.yelp.android',
'com.tumblr',
'com.reddit.frontpage'
];
List<String>appName = [
'Kwali',
'Fast Films',
'All Tencent gaming Apps',
'Chinabrands',
'DX',
'Modlity',
'Woo',
'Badoo',
'Facebook',
'Baidu',
'Heal of Y',
'Vokal',
'Hungama',
'FriendsFeed',
'Private Blogs'
];
apps.removeWhere((element) {
return !packageName.contains(element.packageName) &&
!appName.contains(element.appName);
});
if(apps.isEmpty){
Future.delayed(Duration(seconds: 1),(){
playMusic("a");
});
}
if(apps.isNotEmpty){
Future.delayed(Duration(seconds: 1),(){
playMusic("b");
});
}
return apps.isNotEmpty ? Scrollbar(
child: ListView.builder(
itemCount: apps.length,
itemBuilder: (BuildContext context, int position) {
Application app = apps[position];
if (apps.length == 0) {
Future.delayed(Duration(seconds: 0), () {
setState(() {
isEmpty = true;
});
});
} else if (apps.length > 0) {
return Column(
children: <Widget>[
ListTile(
leading: app is ApplicationWithIcon
? CircleAvatar(
backgroundImage: MemoryImage(app.icon),
backgroundColor: Colors.white,
)
: null,
onTap: () async {
try {
bool isUninstalled =
await AppUninstaller.Uninstall(
app.packageName);
setState(() {
uninstallStatus = isUninstalled
? "Successfully Uninstalled!"
: "Cancelled by user";
});
} on Exception {
uninstallStatus = "Some error occurred";
}
},
title:
Text('${app.appName} (${app.packageName})'),
subtitle: Text('Version: ${app.versionName}\n'
'System app: ${app.systemApp}\n'
'APK file path: ${app.apkFilePath}\n'
'Data dir: ${app.dataDir}\n'
'Installed: ${DateTime
.fromMillisecondsSinceEpoch(
app.installTimeMillis).toString()}\n'
'Updated: ${DateTime.fromMillisecondsSinceEpoch(
app.updateTimeMillis).toString()}'),
trailing: GestureDetector(
child: Icon(
Icons.delete,
size:
0.0,
color: Colors.redAccent,
),
onTap: () async {
try {
bool isUninstalled =
await AppUninstaller.Uninstall(
app.packageName);
setState(() {
uninstallStatus = isUninstalled
? "Successfully Uninstalled!"
: "Cancelled by user";
});
} on Exception {
uninstallStatus = "Some error occurred";
}
},
),
),
const Divider(
height: 1.0,
)
],
);
}
print("else is running");
Future.delayed(Duration(seconds: 0), () {
setState(() {
isEmpty = true;
});
});
return Container();
}
)
) : Screenshot(
controller: _screenshotcontroller,
child: Container(
color: Colors.white,
height: height,
width: width,
child: Center(child: Padding(padding: EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Scan Date:${now.day}/${now.month}/${now.year}",style: TextStyle(
fontSize: 20.0,
),
),
SizedBox(height: height*0.03,),
Text("Scan Time:${now.hour}:${now.minute}:${now.second}",style: TextStyle(
fontSize: 20.0,
),
),
SizedBox(height: height*0.07,),
Text("Your Phone is Secure", style: TextStyle(
color: Colors.red,
fontSize: 30.0,
),
),
],
),
SizedBox(height: height*0.07,),
MaterialButton(
onPressed:() async {
Uint8List uint8List;
await _screenshotcontroller.capture(delay: Duration(milliseconds: 500)).then((Uint8List image){
setState(() {
print("image empty=${image.isEmpty}");
uint8List=image;
});
});
String tempPath = (await getTemporaryDirectory()).path;
File file = File('$tempPath/image.png');
list.add(file.path);
await file.writeAsBytes(uint8List);
await Share.shareFiles(list);
},
minWidth: 280.0,
splashColor: Colors.green[800],
color: Colors.deepPurpleAccent,
padding: EdgeInsets.symmetric(
vertical: 10.0,
),
child: Text(
"SHARE",
style: TextStyle(
fontSize: 22.0,
color: Colors.white,
),
),
),
],
),
),
),
),
);
}
},
);
}
void playMusic(String a) async{
if(a=="a"){
await audioCache.loop("music/ok.wav");
}else{
await audioCache.loop("music/danger.mp3");
}
}
void stop()async{
await audioPlayer.stop();
}
}`
I am trying create banned app uninstaller app
Replace List<Application> apps = data.data; with List<Application>? apps = data.data;
I have a typescript file and having some issues ( I believe re-rendering) inside the cellsrenderer function as shown below:
cellsrenderer: (row, columnfield, value, defaulthtml, columnproperties): string => {
axios
.get("api/personnels/"+value)
.then(response => {
this.setState({
createdByName: response.data.displayedValues
}, ()=> {
console.log('Inside axios response after setting the state to the name of the project creater')
})
}).catch(err => console.log(err));
return this.state.createdByName;
}
When my code runs, I keep seeing the console.log('Inside axios response after setting the state to the name of the project creater') in console for many times even though I have only 30 records to display in total. The browser freezes at some point and I have to close it forcefully. It's happening only because of an API call that I am making inside cellsrenderer function shown above. If I just do the following, everything works fine:
cellsrenderer: (row, columnfield, value, defaulthtml, columnproperties): string => {
return value;
}
What issue is with the API call inside cellsrenderer function which is causing the browser to freeze?
In chrome I am seeing the following:
And then it throws in the browser net::ERR_INSUFFICIENT_RESOURCES related error:
GET https://myserver.com/api/personnels/12345 net::ERR_INSUFFICIENT_RESOURCES
dispatchXhrRequest # xhr.js:160
xhrAdapter # xhr.js:11
dispatchRequest # dispatchRequest.js:59
Promise.then (async)
request # Axios.js:51
Axios.<computed> # Axios.js:61
wrap # bind.js:9
cellsrenderer # Projects.tsx:261
_rendercell # jqxgrid.js:8
_rendervisualcell # jqxgrid.js:8
_rendervisualrows # jqxgrid.js:8
l # jqxgrid.js:8
_renderrows # jqxgrid.js:8
rendergridcontent # jqxgrid.js:8
_render # jqxgrid.js:8
dataview.update # jqxgrid.js:8
q # jqxgrid.js:8
l # jqxgrid.js:8
callDownloadComplete # jqxdata.js:8
success # jqxdata.js:8
bw # jqxcore.js:8
fireWith # jqxcore.js:8
S # jqxdata.js:8
H # jqxdata.js:8
Projects.tsx:269 Error: Network Error
at createError (createError.js:16:15)
at XMLHttpRequest.handleError (xhr.js:69:14)
Here is my complete code:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {FormikApp} from './forms/AddProjectForm'
import JqxGrid, {IGridProps, jqx} from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxgrid';
import JqxButton from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxbuttons'
import {RouteComponentProps} from 'react-router-dom'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.base.css'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.material.css'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.arctic.css'
import {Dialog} from "primereact/dialog";
import {Button} from "primereact/button";
import {properties} from "../properties";
import {Card} from "primereact/card";
import axios from "axios";
import {Messages} from "primereact/messages";
import _ from 'lodash'
export interface IState extends IGridProps {
projects: [],
selectedProject: [],
createdByName :string,
addDialogVisible: boolean,
blazerId: string,
username: string,
selectedRowIndex: number,
deleteDialogVisible: boolean
}
class Projects extends React.PureComponent<RouteComponentProps<{}>, IState> {
private baseUrl = properties.baseUrlWs
private myGrid = React.createRef<JqxGrid>()
private messages = React.createRef<Messages>()
private editrow: number = -1;
constructor(props: RouteComponentProps) {
super(props);
this.selectionInfo = this.selectionInfo.bind(this)
this.gridOnSort = this.gridOnSort.bind(this);
const columns: IGridProps['columns'] = [
{ text: 'Project Name', datafield: 'name', width: 390 },
{ text: 'Project Description', datafield: 'description', width: 390 },
{ text: 'Owner Assigned', datafield: 'institutionId', width: 180,hidden:true },
{ text: 'Created By', datafield: 'createdBy',
cellsrenderer: (row, columnfield, value, defaulthtml, columnproperties): string => {
axios
.get("api/personnels/"+value)
.then(response => {
this.setState({
createdByName: response.data.displayedValues
}, ()=> {
console.log('Inside axios response after setting the state to the name of the project creater')
})
}).catch(err => console.log(err));
return this.state.createdByName;
}
}
]
const source:any = {
dataFields: [
{ name: 'id', type: 'long'},
{ name: 'name', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'url', type: 'string'},
{ name: 'irbProtocol', type: 'string'},
{ name: 'institutionId', type: 'long' },
{ name: 'projectType', type: 'string' },
{ name: 'priority', type: 'string'},
{ name: 'researchDataSetType', type: 'string'},
{ name: 'statusIndicatorId', type: 'long'},
{ name: 'createdBy', type: 'string' }
],
dataType: 'json',
root: 'projects',
sortColumn: 'name',
sortdirection: 'asc',
url: this.baseUrl + 'api/projects/search/getProjectsById',
data: {
value: ''
}
}
const dataAdapter:any = new jqx.dataAdapter(source,
{
autoBind: true,
downloadComplete: (data:any, status:any, xhr:any):void => {
// if (!source.totalrecords) {
source.totalrecords = parseInt(data['page'].totalElements);
// }
},
formatData: (data:any):any => {
data.page = data.pagenum
data.size = data.pagesize
if (data.sortdatafield && data.sortorder) {
data.sort = data.sortdatafield + ',' + data.sortorder;
}
return data;
},
loadError (xhr, status, error) {
throw new Error('Error occurred in getting Projects for user ' + error.toString());
}
}
);
this.state = {
projects: [],
selectedProject: [],
createdByName : '',
blazerId: '',
username: '',
addDialogVisible: false,
selectedRowIndex: null,
deleteDialogVisible: false,
columns: columns,
rendergridrows: (params: any): any[] => {
const data = params.data
return data;
},
source: dataAdapter,
};
}
setValueProperty = (data:any):any => {
if (this.state && this.state.blazerId) {
data.value = this.state.blazerId
}
}
private gridOnSort(event: any): void {
const sortinformation = event.args.sortinformation;
let sortdirection = sortinformation.sortdirection.ascending ? 'ascending' : 'descending';
if (!sortinformation.sortdirection.ascending && !sortinformation.sortdirection.descending) {
sortdirection = 'null';
}
this.myGrid.current.updatebounddata('sort')
};
selectionInfo = (event: any): void => {
const selection = this.myGrid.current.getrowdata(event.args.rowindex)
this.setState({
selectedProject: selection
}, () => {
console.log('pushing ' + this.state.selectedProject)
this.props.history.push({
pathname: '/project',
state: {
project: this.state.selectedProject,
blazerId: this.state.blazerId
}
})
});
}
componentDidMount() {
console.log('In Projects.componentDidMount....' + sessionStorage.getItem('loggedInUser'))
if (sessionStorage.getItem('loggedInUser') != null) {
const loggedInUser = JSON.parse(sessionStorage.getItem('loggedInUser') as string)
this.setState({ employeeId: loggedInUser.employeeId})
}
}
render() {
const defaultView = this.state.addDialogVisible ? null : (this.state.employeeId && !_.isEmpty(this.state.employeeId)) ? (
<div style={{width: '100%', margin: '0 auto', display: 'table'}}>
<JqxGrid
// #ts-ignore
ref={this.myGrid}
theme={'arctic'}
altrows={true}
width="100%"
autoheight={true}
source={this.state.source}
columns={this.state.columns}
pageable={true}
sortable={true}
onSort={this.gridOnSort}
pagesize={20}
virtualmode={true}
rendergridrows={this.state.rendergridrows}
showtoolbar={true}
rendertoolbar={this.state.rendertoolbar}
columnsresize={true}/>
</div>
) : null
return (
<div className="project-page-main">
<Messages ref={this.messages} style={{width: '100%', margin: 'auto' }}/>
<div className="content">
{defaultView}
</div>
</div>
);
}
}
export default Projects;
I removed all the code that wasn't necessary to the explanation.
This is just to give you an idea of how you could move your api call in componentDidMount. There might be some modifications to make since I don't use jQuery with React I might have make false assumptions. In particular, I assumed you could know what values will be fetched in advance.
In you cellrenderer function you will get the value directly from the state:
cellsrenderer: (
row,
columnfield,
value,
defaulthtml,
columnproperties
): string => {
if(typeof this.state?.createdByName[value] === 'undefined') return ''
return this.state?.createdByName[value];
},
A getCreatedByName function to call the api.
getCreatedByName = async (value: string) => {
try {
const response = await axios.get('api/personnels/' + value);
return { response: response.data.displayedValues, value };
} catch (error) {
console.error(error);
return { response: 'error', value };
}
}
On componentDidMount you call your api with your values. You wait for all the api call to resolve so that you don't update the state multiple times.
componentDidMount() {
let promises: Promise<{ value: string; response: string }>[] = [];
const values = [`Value1`, 'Value2'];
values.forEach((value) => {
promises.push(this.getCreatedByName(value));
});
Promise.all(promises).then((responses) => {
const createdByName: any = {};
responses.forEach((response) => {
createdByName[response.value] = response.response;
});
this.setState(
{
createdByName,
},
() => {
console.log(
'Inside axios response after setting the state to the name of the project creater'
);
}
);
});
}
I am working in flutter for few months. I have a problem in Listview items, when i removed a item of listtile using removeWhere fuction from the snapshot.data it reappears within a second.
Can any one help to hide the listtile item permanently from the snap shot.
see how it reappears ->:
This is my code snippet
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:need/credentials.dart';
import 'package:need/models.dart';
import 'package:need/shop/need_details_shop.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
class shopOrders extends StatefulWidget {
const shopOrders({Key key}) : super(key: key);
#override
_shopOrdersState createState() => _shopOrdersState();
}
class _shopOrdersState extends State with AutomaticKeepAliveClientMixin {
Future _updateStatus(String orderId, String orderStatus) async {
String uri = '$domain/shop/order/status/$orderId/$orderStatus';
var response = await http.put(uri, headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
});
if (response.statusCode == 200) {
print("sucess");
} else {
print("failed");
}
}
Future<List<Orders>> _fetchShopOrders() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var sid = preferences.getString('shop_id');
String uri = '$domain/shop/order/view/$sid';
var response = await http.get(uri);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Orders> listOfOrders = items.map<Orders>((json) {
return Orders.fromJson(json);
}).toList();
return listOfOrders;
} else {
throw Exception('Failed to load internet');
}
}
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
body: SingleChildScrollView(
child: Center(
child: Column(children: <Widget>[
Container(
child: FutureBuilder<List<Orders>>(
future: _fetchShopOrders(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
primary: false,
children: snapshot.data
.map((order) => Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 130,
width: 130,
child: Container(
color: Colors.grey,
child: Image(
image: NetworkImage(order.product_photo),
fit: BoxFit.fill),
)),
SizedBox(
width: 20,
),
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(order.product_title.toUpperCase()),
Text("Price " + order.product_price),
Text("Status" + " ..."),
Text(order.ordered_date),
Row(
children: [
IconButton(
icon: Icon(Icons.check,
color: Colors.green),
onPressed: () {
String oid = order.order_id;
String status = "delivered";
_updateStatus(order.order_id, status);
setState(() {
snapshot.data.removeWhere(
(order) => order.order_id == oid);
});
}),
IconButton(
icon:
Icon(Icons.close, color: Colors.pink),
onPressed: () {
String oid = order.order_id;
String status = "cancled";
_updateStatus(order.order_id, status);
setState(() {
snapshot.data.removeWhere(
(order) => order.order_id == oid);
});
})
],
)
],
),
)
],
)))
.toList(),
);
},
))
]))));
}
}
This issue happens because the UI gets rebuilt again each time you remove an item, which means the FutureBuilder is rebuilt again, triggering the API request which causes the item to be brought back, what I would do in a case like that is call the API request inside initstate and save the response, then build the UI based on it, maybe even add a bool that becomes true when the Future is loading and false when. it's done, I'll be happy to help further but I'll let you try it on your own first.
Happy coding!
i get the image from the service class via this code
this.cameraService.getImage(this.width, this.height, this.quality).subscribe(data => this.image = data, error =>
I would want to pass this code to one of the getVision() function so that i am able to use the Google API.May i know how do i do it? I tried declaring a string variable and try to put the above code inside the variable however it does not work.Below are the codes
Camera.TS class
export class CameraPage {
width: number;
height: number;
cropper: Cropper;*/
image:string;
width:number = 500;
height:number = 500;
quality:number = 90;
picture:string;
labels: Array<any> = [];
//translation
scanning: Array<any> = [];
choseLang: boolean = false;
loading: boolean = false;
constructor(public navCtrl: NavController, public navParams: NavParams,public testService: TestService,public cameraService: CameraService,public toastCtrl: ToastController) {
}
addPhoto(){ //take picture & return image ***
this.cameraService.getImage(this.width, this.height, this.quality).subscribe(data => this.image = data, error =>
{
this.getVision(this.image);
// Toast errot and return DEFAULT_PHOTO from Constants
this.toast(error);
});
}
toast(message: string) {
let toast = this.toastCtrl.create({
message: message,
duration: 2500,
showCloseButton: false
});
toast.present();
}
getVision(image64:string) {
this.testService.getVisionLabels(image64:string)
.subscribe((sub) => {
this.labels = sub.responses[0].textAnnotations;
this.getText();
});
}
getText() {
this.labels.forEach((label) => {
let translation = {search: label.description, result: ''};
console.log(label.description);
});
}
}
camera service class
export class CameraService {
public base64Image: string;
constructor(public platform: Platform, public alertCtrl: AlertController, public modalCtrl: ModalController, private http: Http) {
}
getImage(width: number, height: number, quality: number) {
return Observable.create(observer => {
//Set default options for taking an image with the camera
let imageOptions: any = {
quality: quality,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
correctOrientation: 1,
saveToPhotoAlbum: false,
mediaType: Camera.MediaType.PICTURE,
cameraDirection: 1
};
let selectAlert = this.alertCtrl.create({
title: 'Let\'s add a picture!',
message: "Select how you would like to add the picture",
enableBackdropDismiss: false,
buttons: [{
text: 'Albums',
handler: data => {
//Change sourceType to PHOTOLIBRARY
imageOptions.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;
selectAlert.dismiss();
}
}, {
text: 'Camera',
handler: data => {
selectAlert.dismiss();
}
}]
});
selectAlert.onDidDismiss(() => {
this.getCameraImage(imageOptions).subscribe(image => { //image options are either album or camera**
let cropModal = this.modalCtrl.create(ScannerPage, { "imageBase64": image, "width": 500, "height": 500 });
cropModal.onDidDismiss((croppedImage: any) => {
if (!croppedImage)
observer.error("Canceled while cropping.")
else {
observer.next(croppedImage);
observer.complete();
}
});
cropModal.present();
}, error => observer.error(error));
});
selectAlert.present();
});
}
getCameraImage(options: any) { //get base64 image
return Observable.create(observer => {
this.platform.ready().then(() => {
Camera.getPicture(options).then((imageData: any) => {
// imageData is a base64 encoded string as per options set above
let base64Image: string = "data:image/jpeg;base64," + imageData;
observer.next(base64Image);
observer.complete();
}, error => {
observer.error(error);
});
});
});
}
}
You need to understand that the data returned by an Observable service can be accessed in .subscribe() callback 1 and not in callback 2. Read this for more clarification.
this.cameraService.getImage(this.width,this.height,this.quality)
.subscribe( (data) => {
this.image = data;
this.getVision(this.image);
},(error) => {
// Toast errot and return DEFAULT_PHOTO from Constants
this.toast(error);
}
);
I want to add exported component on TouchableHighlight in react-native.
var MessageBox = require('./GiftedMessengerContainer');
var MyAppName = React.createClass({
_openGiftedMessanger(){
return (<MessageBox style={styles.container}/>);
},
render: function() {
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.imButton}
onPress={this._openGiftedMessanger}>
<Text>Open Chat Room</Text>
</TouchableHighlight>
}
</View>
);
}
AppRegistry.registerComponent('MyAppName', () => AppName);
And my module is,
import React, {
Linking,
Platform,
ActionSheetIOS,
Dimensions,
View,
Text,
//Navigator,
Component,
} from 'react-native';
var GiftedMessenger = require('react-native-gifted-messenger');
var Communications = require('react-native-communications');
// var STATUS_BAR_HEIGHT = Navigator.NavigationBar.Styles.General.StatusBarHeight;
// if (Platform.OS === 'android') {
// var ExtraDimensions = require('react-native-extra-dimensions-android');
// var STATUS_BAR_HEIGHT = ExtraDimensions.get('STATUS_BAR_HEIGHT');
// }
class GiftedMessengerContainer extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this._messages = this.getInitialMessages();
this.state = {
messages: this._messages,
isLoadingEarlierMessages: false,
typingMessage: '',
allLoaded: false,
};
}
componentDidMount() {
this._isMounted = true;
setTimeout(() => {
this.setState({
typingMessage: 'React-Bot is typing a message...',
});
}, 1000); // simulating network
setTimeout(() => {
this.setState({
typingMessage: '',
});
}, 3000); // simulating network
setTimeout(() => {
this.handleReceive({
text: 'Hello Awesome Developer',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
});
}, 3300); // simulating network
}
componentWillUnmount() {
this._isMounted = false;
}
getInitialMessages() {
return [
{
text: 'Are you building a chat app?',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(2016, 3, 14, 13, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
{
text: "Yes, and I use Gifted Messenger!",
name: 'Awesome Developer',
image: null,
position: 'right',
date: new Date(2016, 3, 14, 13, 1),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
];
}
setMessageStatus(uniqueId, status) {
let messages = [];
let found = false;
for (let i = 0; i < this._messages.length; i++) {
if (this._messages[i].uniqueId === uniqueId) {
let clone = Object.assign({}, this._messages[i]);
clone.status = status;
messages.push(clone);
found = true;
} else {
messages.push(this._messages[i]);
}
}
if (found === true) {
this.setMessages(messages);
}
}
setMessages(messages) {
this._messages = messages;
// append the message
this.setState({
messages: messages,
});
}
handleSend(message = {}) {
// Your logic here
// Send message.text to your server
message.uniqueId = Math.round(Math.random() * 10000); // simulating server-side unique id generation
this.setMessages(this._messages.concat(message));
// mark the sent message as Seen
setTimeout(() => {
this.setMessageStatus(message.uniqueId, 'Seen'); // here you can replace 'Seen' by any string you want
}, 1000);
// if you couldn't send the message to your server :
// this.setMessageStatus(message.uniqueId, 'ErrorButton');
}
onLoadEarlierMessages() {
// display a loader until you retrieve the messages from your server
this.setState({
isLoadingEarlierMessages: true,
});
// Your logic here
// Eg: Retrieve old messages from your server
// IMPORTANT
// Oldest messages have to be at the begining of the array
var earlierMessages = [
{
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. https://github.com/facebook/react-native',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(2016, 0, 1, 20, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
}, {
text: 'This is a touchable phone number 0606060606 parsed by taskrabbit/react-native-parsed-text',
name: 'Awesome Developer',
image: null,
position: 'right',
date: new Date(2016, 0, 2, 12, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
];
setTimeout(() => {
this.setMessages(earlierMessages.concat(this._messages)); // prepend the earlier messages to your list
this.setState({
isLoadingEarlierMessages: false, // hide the loader
allLoaded: true, // hide the `Load earlier messages` button
});
}, 1000); // simulating network
}
handleReceive(message = {}) {
// make sure that your message contains :
// text, name, image, position: 'left', date, uniqueId
this.setMessages(this._messages.concat(message));
}
onErrorButtonPress(message = {}) {
// Your logic here
// re-send the failed message
// remove the status
this.setMessageStatus(message.uniqueId, '');
}
// will be triggered when the Image of a row is touched
onImagePress(message = {}) {
// Your logic here
// Eg: Navigate to the user profile
}
render() {
return (
<GiftedMessenger
ref={(c) => this._GiftedMessenger = c}
styles={{
bubbleRight: {
marginLeft: 70,
backgroundColor: '#007aff',
},
}}
autoFocus={false}
messages={this.state.messages}
handleSend={this.handleSend.bind(this)}
onErrorButtonPress={this.onErrorButtonPress.bind(this)}
maxHeight={Dimensions.get('window').height} //- Navigator.NavigationBar.Styles.General.NavBarHeight - STATUS_BAR_HEIGHT}
loadEarlierMessagesButton={!this.state.allLoaded}
onLoadEarlierMessages={this.onLoadEarlierMessages.bind(this)}
senderName='Awesome Developer'
senderImage={null}
onImagePress={this.onImagePress}
displayNames={true}
parseText={true} // enable handlePhonePress, handleUrlPress and handleEmailPress
handlePhonePress={this.handlePhonePress}
handleUrlPress={this.handleUrlPress}
handleEmailPress={this.handleEmailPress}
isLoadingEarlierMessages={this.state.isLoadingEarlierMessages}
typingMessage={this.state.typingMessage}
/>
);
}
handleUrlPress(url) {
Linking.openURL(url);
}
// TODO
// make this compatible with Android
handlePhonePress(phone) {
if (Platform.OS !== 'android') {
var BUTTONS = [
'Text message',
'Call',
'Cancel',
];
var CANCEL_INDEX = 2;
ActionSheetIOS.showActionSheetWithOptions({
options: BUTTONS,
cancelButtonIndex: CANCEL_INDEX
},
(buttonIndex) => {
switch (buttonIndex) {
case 0:
Communications.phonecall(phone, true);
break;
case 1:
Communications.text(phone);
break;
}
});
}
}
handleEmailPress(email) {
Communications.email(email, null, null, null, null);
}
}
module.exports = GiftedMessengerContainer;
How to add custom views on my screen?
You need to make use of something called as states (in React terms). When onPress function is invoked you set a state variable to open/close which then can be used to show/hide the custom view. For ex:
var MessageBox = require('./GiftedMessengerContainer');
var MyAppName = React.createClass({
getInitialState: function(){
return {
messageBoxShow: 'false'
}
},
_openGiftedMessanger:function(){
this.setState({
messageBoxShow: 'true'
});
},
render: function() {
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.imButton}
onPress={this._openGiftedMessanger}>
<Text>Open Chat Room</Text>
</TouchableHighlight>
{this.state.messageBoxShow === 'true' ? <MessageBox style={styles.container}/> : null };
</View>
);
}
AppRegistry.registerComponent('MyAppName', () => AppName);