Is there a way we can make the links in Flutter clickable if they are being fetched via JSON API? I mean, I can see that my links get a different color, but when I try to click on it, nothing happens. Trying it on an emulator, have not released the app yet.
JSON:
"content": {
"rendered": "<p>Absolutely great movie Test!</p>\n"},
I need to make sure that "Test" is clickable and sends me to the post in my app.
This is the content JSON file:
class Content {
String? raw;
String? rendered;
bool? protected;
int? blockVersion;
Content({this.rendered});
Content.fromJson(Map<String, dynamic> json) {
raw = json['raw'];
rendered = json['rendered'];
protected = json['protected'];
blockVersion = json['block_version'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['raw'] = this.raw;
data['rendered'] = this.rendered;
data['protected'] = this.protected;
data['block_version'] = this.blockVersion;
return data;
}
}
How can I make them clickable automatically?
#override
void initState() {
super.initState();
_content = widget.post.content?.rendered ?? "";
_content = _content.replaceAll('localhost', '192.168.6.165');
}
The text itself:
Html(
data: _content // this is where all texts are,
// blockSpacing: 0.0,
),
If I use RichText, it gives me the following error:
error: The argument type 'String' can't be assigned to the parameter
type 'InlineSpan'. (argument_type_not_assignable)
RichText(
text: TextSpan(
text: 'Hello ',
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: 'world!',
style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(
text: ' click here!',
recognizer: TapGestureRecognizer()
..onTap = () => print('click')),
],
),
);
Okay, I partially solved it.
Still, the problem I have with this is that it opens a browser - I need it to go to the posts itself on my app, but I can't get this to work. Just sharing anyway.
Html(
data: _content,
onLinkTap: (url, _, __, ___) async {
if (await launch(url!)) {
await launch(
url,
);
}
},
),
Related
I'm new in flutter and I have issue with parsing JSON on HTTP response.
I'm using Airtable backend, to store information about posts. These always contain images, and sometimes attachments - PDFs.
I built PODO, like this:
class Post {
String recordid;
String timecreated;
String title;
String content;
String imgurl;
List<Pdf>? pdf;
Post({
required this.recordid,
required this.timecreated,
required this.title,
required this.content,
required this.imgurl,
required this.pdf
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
// fields: Fields.fromJson(json['fields']),
recordid: json['id'] as String,
timecreated: json['createdTime'] as String,
title: json['fields']['field1'] as String,
content: json['fields']['field2'] as String,
imgurl: json['fields']['IMG'][0]['url'] as String,
pdf: json['fields']['PDF'] == null ? null : List<Map<String, dynamic>>.from(json['fields']['PDF']).map((dynamic value) => Pdf.fromJson(value)).toList()
);
}
Map<String, dynamic> toJson() => {
"recordid": recordid,
"timecreated": timecreated,
"title": title,
"content": content,
"imgurl": imgurl,
"pdf": pdf
// "fields": List<dynamic>.from(fields.map((x) => x.toJson())),
};
}
class Pdf {
Pdf({
required this.url,
required this.filename
});
Pdf.fromJson(Map<String, dynamic> json) :
url = json['url'],
filename = json['filename'];
final String? url;
final String? filename;
}
I'm not getting any errors, but when I'm trying to use PDF URL in UI, eg. in Text:
ListTile(title: Text(post.pdf.url)),
I'm getting error:
The property 'url' can't be unconditionally accessed because the
receiver can be 'null'.
I'm aiming to create a button on a page, that is clickable when PDF URL exists. When it exists, button navigates to PDF view that use PDF URL to get and display PDF.
Any ideas?
The pdf attribute is nullable, hence it cannot be accessed unconditionally. This is assuming you somehow have the pdf not as a list, otherwise you would need to index your list, your code is incomplete. You could try to do something like this:
if (post.pdf != null) {
//wrap it with a button or whatever
return ListTile(title: Text(post.pdf!.url));
}
else {
return Text('no pdf');
}
Well, sth seems to work, but still cannot parse pdf URL.
I'm getting Text "sth is there" when post.pdf != null and it works, but when I'm changing to get value from model using post.pdf!.url I'm getting same error:
Try correcting the name to the name of an existing getter, or defining
a getter or field named 'url'.
child:Text(post.pdf!.url));
Here's piece of code:
LayoutBuilder(builder: (context, constraints) {
if(post.pdf != null){
return ElevatedButton(onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PDFview(pdf: [])
)),
child:Text(post.pdf!.url));
}else{
return ElevatedButton(onPressed: null,
child:Text("no PDF"));
}
}
)
A fix in PODO worked for me.
Here's solution for my problem :)
pdf: json['fields']['PDF'] == null ? null : json['fields']['PDF'][0]['url'] as String,
I was trying to fetch data results from a REST API and then display it in the UI. So everything went well the JSON was parsed well the try and catch method was working fine. But somehow the code was not able to display the parsed results in the UI. Neither gave me an error or exception. I have been struggling to attain the desired result for quite the past few days.
Model Class:
import 'dart:convert';
Transaction transactionFromJson(String str) =>
Transaction.fromJson(json.decode(str));
String transactionToJson(Transaction data) => json.encode(data.toJson());
class Transaction {
Transaction({
required this.dataDescription,
required this.orderStatus,
required this.statusObjects,
});
String dataDescription;
String orderStatus;
List<StatusObject> statusObjects;
factory Transaction.fromJson(Map<String, dynamic> json) => Transaction(
dataDescription: json["data-description"],
orderStatus: json["order-status"],
statusObjects: List<StatusObject>.from(
json["status-objects"].map((x) => StatusObject.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data-description": dataDescription,
"order-status": orderStatus,
"status-objects":
List<dynamic>.from(statusObjects.map((x) => x.toJson())),
};
}
class StatusObject {
StatusObject({
required this.type,
required this.status,
required this.date,
required this.time,
});
String type;
String status;
DateTime date;
String time;
factory StatusObject.fromJson(Map<String, dynamic> json) => StatusObject(
type: json["type"],
status: json["status"],
date: DateTime.parse(json["date"]),
time: json["time"],
);
Map<String, dynamic> toJson() => {
"type": type,
"status": status,
"date": date.toIso8601String(),
"time": time,
};
}
This is how the JSON looks like:
{
"data-description": "This api will return an array of objects to be placed in the order status timeline on the second screen",
"order-status": "Success",
"status-objects": [
{
"type": "Payment",
"status": "completed",
"date": "2021-07-02T00:00:00",
"time": "12:00AM"
},
{
"type": "Units Allocated",
"status": "by Axis",
"date": "2021-07-13T00:00:00",
"time": "12:00AM"
}
]
}
API_Manager where the parsing and fetching took place Service Class
class API_Manager {
static Future<Transaction> getDetails() async {
var client = http.Client();
var transactions;
try {
var response = await client.get(
Uri.https("your api url here"));
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = jsonDecode(jsonString);
transactions = Transaction.fromJson(jsonMap);
}
} catch (e) {
return transactions;
}
return transactions;
}
}
The UI component where I wanted to display the parsed JSON:
Code
FutureBuilder<Transaction>(
future: API_Manager.getDetails(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var data = snapshot.data!.statusObjects;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) =>
Text('$index : ${data[index].status}'),
);
}
return Text('Something was wrong!');
},
),
The output that I am getting is "Something was wrong"
I am quite sure that I have been missing a very small piece of code to make it work. I have been working on this piece of code for quite a few days but am unable to do it. I request you, people, to please help me out in attaining the result or point out the piece of code that I have left out.
Will appreciate it if you could help me in any possible way.
try this
var response = await client
.get(Uri.https("domain", "accounts/test-data/"));
or
var response = await http
.get(Uri.parse("domain/accounts/test-data/"));
This one doesn't work maybe because of the main domain part shouldn't use /, because it indicates subPath,
var response = await client
.get(Uri.https("domain/accounts", "test-data/"));
import 'package:flutter/material.dart';
import 'package:http_request/carditem.dart';
import 'package:http_request/user_data.dart';
// here we chose to have statefull widget because the state needs to be updated and we need to update some variables according to some conditions.
class UserScreen extends StatefulWidget {
const UserScreen({Key key}) : super(key: key);
#override
_UserScreenState createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen> {
// here we created a variable to keep our information in it . this information comes from our user data class where we get the user data from http requests.
Map<String, dynamic> userinfo = {};
// this method is called as soon as the main widget is created . we call our http method here to have the live data as soon as we open the app .be aware that we cant use async and await here . we just call the method.
#override
void initState() {
super.initState();
getData();
}
// here we get hte live data from our userdata class .
Future getData() async {
//we create a userData object to get access to the getuserdata method.
UserData userData = UserData();
//we handle any error here
try {
// here we create a variable to the exact same type we are expecting .
//then we wait for the result of calling our userdata information.
Map<String, dynamic> data = await userData.getUserData();
// we call setState because we need to update a certain value here.
setState(() {
//we assign the the value of data to our variable called userinfo .try the reaosn we see no error behind them is that they are the same type.
userinfo = data;
});
// we catch errors here .
} catch (e) {
print(e);
}
}
// our nice build method ...this method is from our stateful class.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Work Harder'),
centerTitle: true,
),
body: ListView.builder(
itemBuilder: (context, index) => CardItem(
firstName: userinfo['firstName'],
avatarLink: userinfo['avatarLink'],
id: userinfo['id'],
lastName: userinfo['lastName'],
email: userinfo['email'],
),
),
);
}
}
as you can see here is my user_screen dart file the problem is when i hot reload the app is see the renderflex error, while everything seems fine. im a begginner in flutter and i would really appreciate your help.
i have tried singlechild scrollview , list view , expanded widget , but it doesn't work . please help me hotrestart may app without these long errors
import 'package:flutter/material.dart';
// we chose stateless because we don't need to change the state of our app.
class CardItem extends StatelessWidget {
const CardItem({
Key key,
this.firstName,
this.email,
this.avatarLink,
this.id,
this.lastName,
}) : super(key: key);
final String firstName;
final String email;
final String avatarLink;
final String id;
final String lastName;
#override
Widget build(BuildContext context) {
return Card(
color: Colors.lightBlueAccent[200],
elevation: 3,
shadowColor: Colors.blue[100],
child: Column(
children: [
Stack(
children: [
ListTile(
leading: Icon(Icons.check_box),
trailing: CircleAvatar(
child: Image.network(avatarLink),
),
title: Text(firstName),
subtitle: Text(lastName),
),
Positioned(
left: 48,
top: 15,
child: Text(
id,
style: TextStyle(
color: Colors.white70,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
left: 120,
top: 30,
child: Text(
'Email : $email',
style: TextStyle(
color: Colors.blue[900].withOpacity(0.6),
),
),
),
],
),
],
),
);
}
}
here is my card widget , if it helps.
import 'dart:convert';
import 'package:http/http.dart' as http;
// this is the file for our main http requests.
class UserData {
UserData({
this.firstName,
this.lastName,
this.email,
this.id,
this.avatarLink,
});
final String firstName;
final String lastName;
final String email;
final String id;
final String avatarLink;
//this method is response for getting the live data we want also its responsible for decoding it with 'dart convert' library and returning the result as an output.
Future getUserData() async {
//the url we are working with
final String stringUrl = 'https://reqres.in/api/users?page=2';
//sending the request to web and storing the result to a variable. we also have to wait for the results . (asynchronous operation).
http.Response response = await http.get(
Uri.parse(stringUrl),
);
//we create a variable to the exact type of our output . we want to add our items to this map to use it in the main screen.
Map<String, dynamic> userdetails = {};
//here we want to check if our request was successful if so, we continue our operation and if not we throw an error .
if (response.statusCode == 200) {
// here we are decoding the data that we got from our request above to a variable of type map.
Map<String, dynamic> decodedData = jsonDecode(response.body);
//here we try to iterate through a map .. but im not sure if its correct or not .
for (var user in decodedData.values) {
Map<String, dynamic> myusersInfo = {
'firstName': decodedData['data'][0]['first_name'],
'lastName': decodedData['data'][0]['last_name'],
'email': decodedData['data'][0]['email'],
'id': decodedData['data'][0]['id'].toString(),
'avatarLink': decodedData['data'][0]['avatar'],
};
// here we are trying to add the items to the map specified.
userdetails.addEntries(myusersInfo.entries);
return userdetails;
}
} else {
print(response.statusCode);
throw 'Problem with the get request';
}
}
}
the whole goal of this app was to practice some http request.
the last file I have in this app . I just added this maybe it helps you . thanks in advance.
A Column likes to expand until infinity.
This can be solved by putting an intrinsicHeight around the Column as followed:
This class is useful, for example, when unlimited height is available and you would like a child that would otherwise attempt to expand infinitely to instead size itself to a more reasonable height.
IntrinsicHeight(
child: Column(
children: [
],
)
);
https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html
try column inside SingleChildScrollView
I am a newbie in flutter and I'm trying to create 3 dropdown menus from a json file hosted online. Here is a sample of the json file. This is the link to the json file and this is the model class:
class DropdownModel {
DropdownModel({
this.sports,
this.movies,
this.tv,
});
List<Movie> sports;
List<Movie> movies;
List<Movie> tv;
factory DropdownModel.fromJson(Map<String, dynamic> json) => DropdownModel(
sports: List<Movie>.from(json["sports"].map((x) => Movie.fromJson(x))),
movies: List<Movie>.from(json["movies"].map((x) => Movie.fromJson(x))),
tv: List<Movie>.from(json["tv"].map((x) => Movie.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"sports": List<dynamic>.from(sports.map((x) => x.toJson())),
"movies": List<dynamic>.from(movies.map((x) => x.toJson())),
"tv": List<dynamic>.from(tv.map((x) => x.toJson())),
};
}
class Movie {
Movie({
this.id,
this.name,
});
int id;
String name;
factory Movie.fromJson(Map<String, dynamic> json) => Movie(
id: json["id"],
name: json["name"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
};
}
How do I go about it?
EDIT
After searching around got a solution I came across this solution which answers my question
An example would be:
// Let's supose you have a variable of the DropdownModel
DropwdownModel model = DropdownModel(...);
// We store the current dropdown value
// Here you can put the default option
// Since you have Movies with id, I would recommend saving
// the movie value as the dropdownValue
String dropdownValue = '';
// For example, let's use the music
#override
Widget build(BuildContext context) {
// We define the dropdown button
return DropdownButton<String>(
// The value is the selected value, you should control the state
value: model.fromNameFromId(dropdownValue),
// The icon to open the dropdown
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
// On changed is the method that it is called when a dropdown option
// is selected
onChanged: (String? newValue) {
// Here we change the state, as I noted before
// The newValue will be a movie id
setState(() {
dropdownValue = newValue!;
});
},
items: model.movies.map<DropdownMenuItem<String>>(
// We have to iterate through all the movies and return
// a list of DropdownMenuItems
(Movie m) {
return DropdownMenuItem<String>(
value: m.id,
// We display the movie name instead of the id
child: Text(m.name),
);
})
.toList(),
);
}
If you want three dropdowns, you would have to create three DropdownButton! You can find more information in the documentation!
i'm confusing with this kind of json.
because i never see someone explain about this kind of json data.
i get my jsondata from a link, and the json data show like this
{
"data": [
{
"jenis_komoditas": "Gabah IR 64 KP",
"harga": "5400",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah.png"
},
{
"jenis_komoditas": "Gabah IR 64 KG",
"harga": "6200",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah1.png"
}
]
}
it gets error.
can someone help me how to get the data from first json type?
You can parse your full json string with
Payload payload = payloadFromJson(jsonString);
print('${payload.data[0].jenisKomoditas}');
Related Class
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
List<Datum> data;
Payload({
this.data,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String jenisKomoditas;
String harga;
String satuan;
dynamic persen;
String perubahan;
String selisi;
String image;
Datum({
this.jenisKomoditas,
this.harga,
this.satuan,
this.persen,
this.perubahan,
this.selisi,
this.image,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
jenisKomoditas: json["jenis_komoditas"],
harga: json["harga"],
satuan: json["satuan"],
persen: json["persen"],
perubahan: json["perubahan"],
selisi: json["selisi"],
image: json["image"],
);
Map<String, dynamic> toJson() => {
"jenis_komoditas": jenisKomoditas,
"harga": harga,
"satuan": satuan,
"persen": persen,
"perubahan": perubahan,
"selisi": selisi,
"image": image,
};
}
full code
import 'package:flutter/material.dart';
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
List<Datum> data;
Payload({
this.data,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String jenisKomoditas;
String harga;
String satuan;
dynamic persen;
String perubahan;
String selisi;
String image;
Datum({
this.jenisKomoditas,
this.harga,
this.satuan,
this.persen,
this.perubahan,
this.selisi,
this.image,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
jenisKomoditas: json["jenis_komoditas"],
harga: json["harga"],
satuan: json["satuan"],
persen: json["persen"],
perubahan: json["perubahan"],
selisi: json["selisi"],
image: json["image"],
);
Map<String, dynamic> toJson() => {
"jenis_komoditas": jenisKomoditas,
"harga": harga,
"satuan": satuan,
"persen": persen,
"perubahan": perubahan,
"selisi": selisi,
"image": image,
};
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String jsonString = '''
{
"data": [
{
"jenis_komoditas": "Gabah IR 64 KP",
"harga": "5400",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah.png"
},
{
"jenis_komoditas": "Gabah IR 64 KG",
"harga": "6200",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah1.png"
}
]
}
''';
void _incrementCounter() {
Payload payload = payloadFromJson(jsonString);
print('${payload.data[0].jenisKomoditas}');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Output
I/flutter (23421): Gabah IR 64 KP
you are getting data in form of json array so you can simply call it iteratively from the object. like this
if you get data in data variable:
final jsondata=json.decode(data);
for (var dataf in jsondata['data']){
print(dataf['jenis_komoditas']);
}