Learning Flutter: RxDart , Api calls,WebView & ListView

In this article, I will explain how to use rxdart, call api , use webview and listview with the help of an app i developed which uses recipepuppy api to search for ingredients and recipe of any food items searched by user…

this article is for developers who have basic knowledge simple widgets in flutter…

Final Output:

Create a Searchbar and observe it using RxDart:

first we will have to add RxDart dependency to our pubspec file

rxdart: 0.16.7

once we get that dependency we can make our flutter app reactive , Android developers: RxDart is similar to rxjava and livedata , if you are not familiar with concept of reactive programming here is a link you can refer to

here in this code we will create an searchbar and observe it for any changes…

final subject = new PublishSubject<String>();

bool _isLoading = false;
TextEditingController textEditingController = TextEditingController(text: '');
//this method will be called everytime there is an change of text in our search bar
void _textChanged(String text) {
if (text.isEmpty) {
setState(() {
_isLoading = false;
});
_clearList();
return;
}
setState(() {
_isLoading = true;
});
_clearList();
//call api here
}
@override
void
initState() {
super.initState();
//this will be called at start of the activity,it will create a stream which will listen to change in text of our search bar
  subject.stream
.debounce(new Duration(milliseconds: 600))
.listen(_textChanged);
}
//create search bar
Widget _createSearchBar(BuildContext context) {
return new Card(
child: Row(
children: <Widget>[
new IconButton(
icon: Icon(Icons.arrow_back),
//clearData method will be called when this button will be pressed
            onPressed: () => clearData(),
),
new Expanded(
child: TextField(
autofocus: true,
controller: textEditingController,
decoration: new InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.all(16.0),
hintText: 'Search Recipies here',
),
onChanged: (string) => (subject.add(string)),
))
],
));
}
clearData(){
subject.add("");
textEditingController.text="";
}

Call Api and Parse the Json Data:

In Order to call the api and parse the data we will have to create a podo(plain old dart object) class

//a podo(plain old dart object) class
class Recipe {
String title, thumbnail, href, ingredients;
Recipe(
{this.title,
this.href,
this.ingredients,
this.thumbnail,});

Recipe.fromJson(dynamic recipe) {

try {
this.title = recipe['title'];
this.href=recipe['href'];
this.ingredients = recipe['ingredients'];
this.thumbnail = (recipe['thumbnail']);
if(recipe['thumbnail'].toString().length==0){
this.thumbnail = 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/No_image_available_600_x_450.svg/600px-No_image_available_600_x_450.svg.png';
}

} catch (e) {
print("something happened"+e.toString());
}
}
}

and then add api call in _textChanged

http
.get("http://www.recipepuppy.com/api/?q=$text") //here $text will take text from our search bar
.then((response) => response.body)
.then(JSON.decode)
.then((map) => map["results"])
.then((list) {
list.forEach(_addItem); //it adds all the items to our list
})
.catchError(_onError)
.then((e) {
setState(() {
_isLoading = false;
});
});

create a listview with the data recived from the api:

List<Recipe> _items = new List();
//clear all items from the list
void _clearList() {
setState(() {
_items.clear();
});
}

//add an item to the list
void _addItem(item) {
setState(() {
_items.add(Recipe.fromJson(item));
});
}
//creates view for each item in listview
Widget _createRecipeItem(BuildContext context, Recipe recipe) {
return new GestureDetector( //listens for on tap
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RecipeWeb(url: recipe.href,item: recipe.title),//goes to the next page & passes value of url and title to it
),
);
},
child: Column(
children: <Widget>[
Padding(
padding: new EdgeInsets.all(8.0),
child: new Row(
children: <Widget>[
new Image.network(recipe.thumbnail,
height: 80.0, width: 80.0, fit: BoxFit.fitHeight),
Expanded(
child: Container(
height: 80.0,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: _createRecipeItemDescriptionSection(context, recipe),
)),
],
)),

new Divider(height: 15.0,color: Colors.black,),

],
),
);
}

Widget _createRecipeItemDescriptionSection(BuildContext context, Recipe recipe) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
recipe.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
SizedBox(height: 10.0),
Text(
recipe.ingredients,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.0,
),
),
],
);
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(600.0),
child: const Text(''),
),
body: new Container(
padding:
new EdgeInsets.only(top: 16.0, left: 8.0, right: 8.0, bottom: 8.0),
child: new Column(
children: <Widget>[
_createSearchBar(context),
new Expanded(
child: Card(
child: _isLoading
? Container(
child: Center(child: CircularProgressIndicator()),
padding: EdgeInsets.all(16.0),
)
: new ListView.builder(
padding: new EdgeInsets.all(8.0),
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) {
return _createRecipeItem(context, _items[index]);
},
),
),
)
],
),
),
);
}

Add WebView on next Screen

To add webView first we will have to add dependency for it in our pubspec file

flutter_webview_plugin: ^0.2.1+2

then add this code , it will take url and name of food item clicked from previous screen and display in webview

class RecipeWeb extends StatelessWidget {
static String tag = 'recipie-web';
final String url;
final String item;

//it will take 2 parameters(url & title) from previous screen
RecipeWeb({Key key, @required this.url,@required this.item}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Recipe for $item"),
),
body:new MaterialApp(
routes: {
"/": (_) => new WebviewScaffold(
url: url,
appBar: new AppBar(
title: new Text("Recipe"),
),
)
},
)
);
}
}

you can checkout the full code for this here

Thank you for reading! Feel free to say hi or share your thoughts on Twitter @that_kushal_guy or in the responses below!

you can checkout the full project source code here: https://github.com/kushal2011/recipe_finder