MVVM Design Pattern in Flutter - android

we try to develop a flutter app and we create a stateful widget as a page .
we want to separate build function from other state variable and state function in 2 different file that build function can access to this of state class
we creating a class :
PageClassState extend State<PageClass>{
string value = 'string value';
}
and extend it in a new class that can access PageClassState this variable
we write :
PageClassView extend PageClassState{
#override
Widget Build(){
return(new Text(this.value))
}
}
but in PageClassState we get an error say we must override build method in the class . is there any suggestion to fix the problem and implement MVVM Design pattern in flutter?

I suggest moving your ViewModel code into a separate class that does not extend State. Keep the ViewModel platform independent.
Your Widgets state can have an instance of the viewModel and interact with it.
You can find a more detailed example here
If child Widgets need to access your ViewModel you can use a Inherited Widget as suggested by #RĂ©mi Rousselet.
I quickly implemented this for you:
class ViewModelProvider extends InheritedWidget {
final ViewModel viewModel;
ViewModelProvider({Key key, #required this.viewModel, Widget child})
: super(key: key, child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static ViewModel of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(ViewModelProvider) as
ViewModelProvider).viewModel;
}
Child widgets can grab the ViewModel by calling
var viewModel = ViewModelProvider.of(context);
Let me know if you have any questions :)

That's not the proper approach. You shouldn't split State<T> and it's build method.
The thing is, don't extend widgets. Compose them.
A correct way to achieve something similar is to use InheritedWidget. These will hold you data but do nothing else. And it's children will be able to request those datas using a MyInherited.of(context).
You could also create a builder. Something like :
typedef Widget MyStateBuilder(BuildContext context, MyStateState state);
class MyState extends StatefulWidget {
final MyStateState builder;
const MyState({this.builder}) : assert(builder != null);
#override
MyStateState createState() => new MyStateState();
}
class MyStateState extends State<MyState> {
String name;
#override
Widget build(BuildContext context) {
return widget.builder(context, this);
}
}

I have been using this plugin maintaining large scale application for flutter.mvvm_flutter
https://pub.dev/packages/mvvm_flutter
it's very light and easy to use check some example . its very easy to maintain ui away from business logic's

The mvvm package, A Flutter MVVM (Model-View-ViewModel) implementation.
import 'package:flutter/widgets.dart';
import 'package:mvvm/mvvm.dart';
import 'dart:async';
// ViewModel
class Demo1ViewModel extends ViewModel {
Demo1ViewModel() {
// define bindable property
propertyValue<String>(#time, initial: "");
// timer
start();
}
start() {
Timer.periodic(const Duration(seconds: 1), (_) {
var now = DateTime.now();
// call setValue
setValue<String>(#time, "${now.hour}:${now.minute}:${now.second}");
});
}
}
// View
class Demo1 extends View<Demo1ViewModel> {
Demo1() : super(Demo1ViewModel());
#override
Widget buildCore(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 100),
padding: EdgeInsets.all(40),
// binding
child: $.watchFor(#time,
builder: $.builder1((t) =>
Text(t, textDirection: TextDirection.ltr))));
}
}
// run
void main() => runApp(Demo1());
full example

For ref look at my github https://github.com/anandh-ps/flutter_mvvm_example
MediaService.dart
import 'dart:convert';
import 'dart:io';
import 'package:meta/meta.dart';
import 'package:http/http.dart' as http;
import 'package:mvvm_flutter_app/model/apis/app_exception.dart';
class MediaService {
final String _baseUrl = "https://itunes.apple.com/search?term=";
Future<dynamic> get(String url) async {
dynamic responseJson;
try {
final response = await http.get(_baseUrl + url);
responseJson = returnResponse(response);
} on SocketException {
throw FetchDataException('No Internet Connection');
}
return responseJson;
}
#visibleForTesting
dynamic returnResponse(http.Response response) {
switch (response.statusCode) {
case 200:
dynamic responseJson = jsonDecode(response.body);
return responseJson;
case 400:
throw BadRequestException(response.body.toString());
case 401:
case 403:
throw UnauthorisedException(response.body.toString());
case 500:
default:
throw FetchDataException(
'Error occured while communication with server' +
' with status code : ${response.statusCode}');
}
}
}
MediaRepository.dart
import 'package:mvvm_flutter_app/model/media.dart';
import 'package:mvvm_flutter_app/model/services/media_service.dart';
class MediaRepository {
MediaService _mediaService = MediaService();
Future<List<Media>> fetchMediaList(String value) async {
dynamic response = await _mediaService.get(value);
final jsonData = response['results'] as List;
List<Media> mediaList =
jsonData.map((tagJson) => Media.fromJson(tagJson)).toList();
return mediaList;
}
}
MediaViewModel.dart
import 'package:flutter/cupertino.dart';
import 'package:mvvm_flutter_app/model/apis/api_response.dart';
import 'package:mvvm_flutter_app/model/media.dart';
import 'package:mvvm_flutter_app/model/media_repository.dart';
class MediaViewModel with ChangeNotifier {
ApiResponse _apiResponse = ApiResponse.loading('Fetching artist data');
Media _media;
ApiResponse get response {
return _apiResponse;
}
Media get media {
return _media;
}
/// Call the media service and gets the data of requested media data of
/// an artist.
Future<void> fetchMediaData(String value) async {
try {
List<Media> mediaList = await MediaRepository().fetchMediaList(value);
_apiResponse = ApiResponse.completed(mediaList);
} catch (e) {
_apiResponse = ApiResponse.error(e.toString());
print(e);
}
notifyListeners();
}
void setSelectedMedia(Media media) {
_media = media;
notifyListeners();
}
}
HomeScreen.dart
import 'package:flutter/material.dart';
import 'package:mvvm_flutter_app/model/apis/api_response.dart';
import 'package:mvvm_flutter_app/model/media.dart';
import 'package:mvvm_flutter_app/view/widgets/player_list_widget.dart';
import 'package:mvvm_flutter_app/view/widgets/player_widget.dart';
import 'package:mvvm_flutter_app/view_model/media_view_model.dart';
import 'package:provider/provider.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
final _inputController = TextEditingController();
ApiResponse apiResponse = Provider.of<MediaViewModel>(context).response;
List<Media> mediaList = apiResponse.data as List<Media>;
return Scaffold(
appBar: AppBar(
title: Text('Media Player'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 20.0),
decoration: BoxDecoration(
color: Theme.of(context).accentColor.withAlpha(50),
borderRadius: BorderRadius.circular(30.0),
),
child: TextField(
style: TextStyle(
fontSize: 15.0,
color: Colors.grey,
),
controller: _inputController,
onChanged: (value) {},
onSubmitted: (value) {
if (value.isNotEmpty) {
Provider.of<MediaViewModel>(context)
.setSelectedMedia(null);
Provider.of<MediaViewModel>(context,
listen: false)
.fetchMediaData(value);
}
},
decoration: InputDecoration(
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
hintText: 'Enter Artist Name',
)),
),
),
],
),
),
mediaList != null && mediaList.length > 0
? Expanded(
child: PlayerListWidget(mediaList, (Media media) {
Provider.of<MediaViewModel>(context)
.setSelectedMedia(media);
}))
: Expanded(
child: Center(
child: Text('Search the song by Artist'),
),
),
if (Provider.of<MediaViewModel>(context).media != null)
Align(
alignment: Alignment.bottomCenter,
child: PlayerWidget(
function: () {
setState(() {});
},
)),
],
));
}
}

Related

Flutter setState() not updating the view after Invoking Flutter Code From Native Side

I am trying to implement invoking Flutter Code From Native Side using method channel and working as expected. But having issue with rendering the view after trying to set the state. Can any one help to fix the issue?
Actually the SimSlotInfo is calling from the below widget,
List<Step> getSteps() {
return <Step>[
Step(
state: currentStep > 0 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 0,
title: const Text("Send SMS"),
content: Column(
children: [
SimSlotInfo()
],
),
),
];
}
SimSlotInfo dart class
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutterdemo/model/device_slot.dart';
class SimSlotInfo extends StatefulWidget {
//callback function
final void Function(String) callBackFunction;
const SimSlotInfo(this.callBackFunction, {super.key});
//const SimSlotInfo({Key? key}) : super(key: key);
#override
State<SimSlotInfo> createState() => _SimSlotInfoState();
}
class _SimSlotInfoState extends State<SimSlotInfo> {
final platformMethodChannel = const MethodChannel('common_lib_plugin');
List<SimDetails> simDetailsObj = [];
//execute the below code while page loading
#override
void initState() {
super.initState();
platformMethodChannel.setMethodCallHandler(handleNativeMethodCall);
}
Future<void> handleNativeMethodCall(MethodCall call) async {
// do some processing
switch(call.method) {
case "deviceInfo":
var simData = call.arguments;
var arrayObjsText = '[{"slot":0,"simno":"89911017061","deviceid":"3518920","carrierName":"Vodafone"},{"slot":1,"simno":"89101706","deviceid":"3511643","carrierName":"JIO"}]';
List simObjsJson = jsonDecode(arrayObjsText) as List;
simDetailsObj = simObjsJson.map((tagJson) => SimDetails.fromJson(tagJson)).toList();
setState(() {
simDetailsObj = simDetailsObj;
});
}
}
#override
Widget build(BuildContext context) {
return Column(
children:
simDetailsObj.map((data) => RadioListTile(
dense: true,
contentPadding: EdgeInsets.zero,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${data.carrierName}",
style: const TextStyle(color: Colors.black, fontSize: 18),
),
],
),
groupValue: _selectedSim,
value: data.simno,
onChanged: (val) {
},
)).toList()
);
}
}
First, you are trying to assign List to List so your code is getting brake there. to solve that loop the object with SimDetails object. and that will do the trick
ParentWidget
class _ParentWidgetState extends State<ParentWidget> {
#override
Widget build(BuildContext context) {
return ChildWidget( // <---- child widget
callSetState: (list) { // <--- callback Function
print(list);
setState(() {
// <---
});
},
);
}
}
In Child widget
class ChildWidget extends StatefulWidget {
const ChildWidget({Key? key, required this.callSetState}) : super(key: key);
final Function(List<SimDetails>) callSetState; // <-- declare callback function here
#override
State<ChildWidget> createState() => _ChildWidgetState();
}
and replace your setState with widget.callSetState
Future<void> handleNativeMethodCall(MethodCall methodCall) async {
switch (call.method) {
case 'deviceInfo':
var simData = call.arguments;
var arrayObjsText =
'[{"slot":0,"simno":"89911017061","deviceid":"3518920","carrierName":"Vodafone"},{"slot":1,"simno":"89101706","deviceid":"3511643","carrierName":"JIO"}]';
for (var data in jsonDecode(arrayObjsText)) {
simDetailsObj.add(
SimDetails(
slot: data['slot'],
simno: data['simno'],
deviceid: data['deviceid'],
carrierName: data['carrierName'],
),
);
}
/// setState(() {});
widget.callSetState(simDetailsObj);
break;
default:
}}

Flutter The method 'map' was called on null. Receiver: null error

I'm having this super annoying issue of being unable to grab and display a table from my server hosted on PhpmyAdmin. (I've managed to grab the data and have it printed in the console, but now that I'm trying to display it in a table I can't seem to get it working)
I've tried nulling my variables but I'm not really sure what the main culprit for this error is. Any help would be greatly appreciated.
Image of Error
data.dart File
class dataListing extends StatefulWidget {
const dataListing({Key? key}) : super(key: key);
#override
State<dataListing> createState() => _dataListingState();
}
class _dataListingState extends State<dataListing> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class listingData{
String? ListingID, listingName, listingDescription, address, suburbName, phoneNumber, openingHours, Email, Website;
listingData({
this.ListingID,
this.listingName,
this.listingDescription,
this.address,
this.suburbName,
this.phoneNumber,
this.openingHours,
this.Email,
this.Website,
});
//constructor
List<listingData> datalist = [];
factory listingData.fromJSON(Map<String, dynamic> json){
return listingData(
ListingID: json["ListingID"],
listingName: json["listingName"],
listingDescription: json["listingDescription"],
address: json["address"],
suburbName: json["suburbName"],
phoneNumber: json["phoneNumber"],
openingHours: json["openingHours"],
Email: json["Email"],
Website: json["Website"],
);
}
}
Directory.dart file
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:app/pages/data.dart';
class directoryPage extends StatefulWidget {
#override
State<directoryPage> createState() => _directoryPageState();
}
class _directoryPageState extends State<directoryPage> {
// List serviceListing = [];
//
// getAllListing()async{
// String url = "URL HERE";
// var response = await http.get(Uri.parse(url));
// if (response.statusCode == 200){
// setState (() {
// serviceListing = json.decode(response.body);
// });
// print (serviceListing);
// return serviceListing;
// }
// }
bool error = false, dataloaded = false;
var data;
String dataurl = "URL HERE";
#override
void initState (){
loaddata();
super.initState();
// getAllListing();
}
void loaddata() {
Future.delayed(Duration.zero,() async {
var res = await http.post(Uri.parse(dataurl));
if (res.statusCode == 200) {
setState(() {
data = json.decode(res.body);
dataloaded = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Directory'),
centerTitle: true,
elevation: 0,
backgroundColor: Color(0xFFA30B32),
//WSU Appbar Icon
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset("assets/wsulogo.png", scale: 8.0),
),
),
body: Container(
padding: EdgeInsets.all(15),
child:dataloaded?datalist():
Center(
child:CircularProgressIndicator()
),
)
);
}
Widget datalist(){
if(data["error"]) {
return Text(data["errmsg"]);
}else{
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
);
return Table( //if data is loaded then show table
border: TableBorder.all(width:1, color:Colors.black45),
children: datalist.map((listingdata){
return TableRow( //return table row in every loop
children: [
//table cells inside table row
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.ListingID!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingName!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingDescription!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.address!)
)
),
]
);
}).toList(),
);
}
}
}
Looks like the issue was actually unrelated to the dart side of things, the php code wasn't properly structuring the data. Cannot have underscores or spaces.
Correct-> $json["dballlisting"] = array (); (I renamed it to just "data" later)
Incorrect->$json["db_all_listing"] = array ();
The error seems to be originating from this line, the data['data'] is null which is expected to be an Array.
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
You need to investigate your API call to make sure why it is happening. If the null value is expected then you need to add safeguards in your code to make sure it won't break when it encounter such scenarios. You can add null safety checks for that one way to do it would be to
List<listingData> datalist = List<listingData>.from((data["data"] ?? []).map((i){
return listingData.fromJSON(i);
})

How to keep the old data in same state while emitting the same state with different data and render them separately using different blocBuilders in UI

I have a bloc called specialityBloc which will fetch doctors according to their speciality from firestore by delegating it to a repository.
The problem is when I want to fetch different doctors with different speciality and emit them from same state(fetchSuccessState).
I am able to fetch them separately from firestore but when It comes to rendering in the UI using different bloc builders(that listen to the same specialityBloc).It overrides the old data(which was there in the first Api call) and replaces it with the result of the subsequent api calls.
I want to keep the old the in the state and render it UI and also render the new Data below it(which was emitted from the same state).
Here is my Specialitybloc
SpecialityBloc() : super(InitialState()){
on<GetSpeciality>((event, emit) async {
emit(WaitingSpeciality());
try{
final data = await FirebaseRepo.getDoctorsBySpeciality(event.speciality);
data.fold((l){
emit(GetSpecialitySuccess(doctors:l));
}, (r){
});
}catch(e){
print(e);
}
});
}
}
abstract class SpecialityState extends Equatable {
}
abstract class SpecialityEvent {
}
class InitialState extends SpecialityState {
#override
List<Object> get props => [];
}
class WaitingSpeciality extends SpecialityState {
#override
List<Object> get props => [];
}
class GetSpecialitySuccess extends SpecialityState {
final List<DoctorModel> doctors;
GetSpecialitySuccess({required this.doctors});
#override
List<Object> get props => [doctors];
}
class GetSpeciality extends SpecialityEvent {
final String speciality;
GetSpeciality(this.speciality);
}```
This is the UI part
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:patient_app/ui/screens/home/home_bloc/popular_bloc.dart';
import 'package:patient_app/ui/screens/home/home_bloc/speciality_bloc.dart';
import 'package:patient_app/ui/widgets/gridViewLoading.dart';
import 'package:shimmer/shimmer.dart';
import 'package:patient_app/ui/screens/home/home_bloc/home_bloc.dart';
import 'package:patient_app/ui/widgets/custom_carousel.dart';
import 'package:patient_app/ui/widgets/search_bar.dart';
import 'package:patient_app/ui/widgets/square.dart';
import 'package:patient_app/ui/widgets/username.dart';
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
context.read<HomeBloc>().add(GetUserInfo());
context.read<PopularBloc>().add(GetPopularDoctors());
context.read<SpecialityBloc>().add(GetSpeciality('paediatrics'));
context.read<SpecialityBloc>().add(GetSpeciality('gynaecologist'));
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left:20.0,right: 20,),
child: Column(
children: [
const SizedBox(height: 35,),
BlocBuilder<HomeBloc,HomeState>(
builder: (ctx,state){
if(state is Waiting){
return Align(
alignment: Alignment.centerLeft,
child: Shimmer.fromColors(
baseColor: Colors.amber,
highlightColor: Colors.grey[300]!,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.amber,
),
height: 20,width: 150,),
));
}
if(state is Success){
return UserName(name: state.data.name!);
}
else{
return Container();
}
},
),
CustomCarousel(slides: [
SizedBox(
width: double.infinity,
child: Image.network("https://cdn.pixabay.com/photo/2020/09/13/20/24/doctor-5569298_960_720.png",fit: BoxFit.cover,),
),
SizedBox(
width: double.infinity,
child: Image.network("https://cdn.pixabay.com/photo/2021/11/20/03/16/doctor-6810750_960_720.png",fit: BoxFit.cover,),
),
]),
const SearchBar(),
BlocBuilder<PopularBloc,PopularState>(builder: (ctx,state){
if(state is WaitingPopular){
return const GridViewLoading();
}
if(state is PopularDoctorsSuccess){
return Square(doctors: state.doctors,title: "Popular Doctors",);
}
return Container();
}),
BlocBuilder<SpecialityBloc,SpecialityState>(builder: (ctx,state){
if(state is WaitingSpeciality){
return const GridViewLoading();
}
if(state is GetSpecialitySuccess){
return Square(doctors: state.doctors,title: " Paediatrics",);
}
return Container();
}),
BlocBuilder<SpecialityBloc,SpecialityState>(builder: (ctx,state){
if(state is WaitingSpeciality){
return const GridViewLoading();
}
if(state is GetSpecialitySuccess){
return Square(doctors: state.doctors,title: "Gynaecologist",);
}
return Container();
})
],
),
),
);
}
}
if I got you correctly you are trying to save the old result that you got from the API to use it later on right ?
if so then :
you can try to make you var a global variable and then when the data comes you can assign the data you got in this variable to use it later on in another state like that
late final data;
//or the other way you can make data as a list here
//of the type object you are using ex Doctor or so..
SpecialityBloc() : super(InitialState()){
on<GetSpeciality>((event, emit) async {
emit(WaitingSpeciality());
try{
data = await FirebaseRepo.getDoctorsBySpeciality(event.speciality);
data.fold((l){
emit(GetSpecialitySuccess(doctors:l));
}, (r){
});
}catch(e){
print(e);
}
});
}
// now you can use your data variable that has the info with any other state or with whatever you want
and just add them to your list each time and then you can get back to whatever object you want from the list like maybe search for an a doctor using some ID and if it's not in the list you can fetch it and add it again to the list and here you can use the same state to render a different object, also try to remove the equatable from your code and try without it if you don't really need it.
If I am getting it right, the simplest and the right approach would be to have different events returning different states for each specialty, while acting on the same or different builders on the UI; based on buildWhen method of the bloc builder. Internally you can write a reusable method which does the API query, trigger it each time with a different event (specialty) and it can emit a respective state based on the method caller.
Benefits: better control on the UI and better testability.

How to use the text field data and calculate through the api call in flutter?

Actually today I decided to make a love calculator and for that I learnt the http call in flutter but now I am stuck at a point and I don't know how to move onwards.
import 'package:AllInOneCalci/Post.dart';
import 'package:AllInOneCalci/customAppBar.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
class LoveCalUI extends StatelessWidget {
#override
Widget build(BuildContext context) {
var AppBarHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: customAppBar(
height: (AppBarHeight / 3) * 0.4,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 18.0),
child: Text(
'All In One Cali',
style: TextStyle(
color: Colors.black,
fontSize: 35.0,
fontFamily: 'DancingScript',
fontWeight: FontWeight.bold),
),
),
],
),
),
body: CustomFetchData(),
);
}
}
class CustomFetchData extends StatefulWidget {
#override
_CustomFetchDataState createState() => _CustomFetchDataState();
}
class _CustomFetchDataState extends State<CustomFetchData> {
Future<Post> getData() async {
final response =
await http.get('https://love-calculator.p.rapidapi.com/getPercentage?fname=John&sname=Alice');
if (response.statusCode == 200) {
return Post.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load api');
}
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: FutureBuilder<Post>(
future: getData(),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text('Please Wait while its loading...'));
} else {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
return Center(
child: Text('${snapshot.data}'),
);
}
}
})),
],
);
}
#override
// ignore: must_call_super
void initState() {
getData();
}
}
This is a lovecalculator class where I am coding all the stuffs. If there is any NEWS website, where the data is fetching from the api call then it will be very easy.
But I want that I enter two names in the text field and the process is now calculated through api i.e the logic is written at the backend and I want to fetch that logic into my code ()given that I HAVE TO ENTER THE NAMES. Can you help me please. I have given that api reference here. If anyone could help me, then it will be very supporting.
api reference- https://rapidapi.com/ajith/api/love-calculator
I assume that you already can use text field controllers and extract text. The thing you needed to include to your request are headers which include important key information.
If you look here, you can find reference for several languages request example (for example I used Swift for reference). Below you can see how to do it in Flutter:
In your pubspec.yaml
http: ^0.12.2
In your .dart file you need to import
import 'dart:convert';
import 'package:http/http.dart' as http;
Also you need to pick a place which would call a function, in my case it was a button from default Flutter starter project:
Map<String, String> requestHeaders = {
'x-rapidapi-host': 'love-calculator.p.rapidapi.com',
'x-rapidapi-key': 'insert your API key from website',
};
void _getNames({String name1, String name2}) async {
final response = await http.get(
'https://love-calculator.p.rapidapi.com/getPercentage?fname=$name1&sname=$name2',
// Send authorization headers to the backend.
headers: requestHeaders,
);
final responseJson = json.decode(response.body);
print(responseJson);
}

Does Flutter recycle images in a ListView?

I would like to make an endlessFeed in Fluter but the app terminates without giving me any information why. Basically it happens after I scrolled down about 60 images then it starts to lag a bit and it crashes.
I tested another API but the same there. It uses images with lower resolution so it takes longer to scroll down until it stops working.
So I don't know what happens here. My guess is that there are to many images in the ListView so that the phone can't handle it and crashes.
I've put the whole code below because I don't even know wehere the problem could be. Is there maybe another way to achieve an endlessImageFeed?
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:cached_network_image/cached_network_image.dart';
// my base url
String imageUrl = "http://192.168.2.107:8000";
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'EndlessFeed',
theme: ThemeData(
primaryColor: Colors.white,
),
home: PhotoList(),
);
}
}
class PhotoList extends StatefulWidget {
#override
PhotoListState createState() => PhotoListState();
}
class PhotoListState extends State<PhotoList> {
StreamController<Photo> streamController;
List<Photo> list = [];
#override
void initState() {
super.initState();
streamController = StreamController.broadcast();
streamController.stream.listen((p) => setState(() => list.add(p)));
load(streamController);
}
load(StreamController<Photo> sc) async {
// URL for API
String url = "http://192.168.2.107:8000/api/";
/* ______________________________________________
I also tried another API but it chrashes also (but it takes longer until crash):
String url = "https://jsonplaceholder.typicode.com/photos/";
______________________________________________ */
var client = new http.Client();
var req = new http.Request('get', Uri.parse(url));
var streamedRes = await client.send(req);
streamedRes.stream
.transform(UTF8.decoder)
.transform(json.decoder)
.expand((e) => e)
.map((map) => Photo.fromJsonMap(map))
.pipe(sc);
}
#override
void dispose() {
super.dispose();
streamController?.close();
streamController = null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("EndlessFeed"),
),
body: Center(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) => _makeElement(index),
),
),
);
}
Widget _makeElement(int index) {
if (index >= list.length) {
return null;
}
return Container(
child: Padding(
padding: EdgeInsets.only(top: 20.0),
child: Column(
children: <Widget>[
child: new Container(
// my base URL + one image
child: new Image(image: new CachedNetworkImageProvider(imageUrl + list[index].mImage))
),
),
],
),
));
}
}
class Photo {
final String mImage;
Photo.fromJsonMap(Map map)
: mImage= map['mImage'];
}

Categories

Resources