Creating a responsive UI

On the home page of our app, we want to show the user a text field to search for any book from the Google Books API library. The results will be shown under the search box, and the appearance of the results will depend on the screen. From here, the user will be able to add a book to their favorites. Let's look at the steps, as follows:

  1. Replace the default example code in the main.dart file with the following:

import 'package:flutter/material.dart'; 
import './data/bookshelper.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Books',
      theme: ThemeData(
        primarySwatch: Colors.blueGrey,
      ),
      home: MyHomePage(),
    );  } }

Please note that in the preceding code, we have imported the bookshelper.dart file and changed the theme colors and the title for the MateriaLApp.

  1. Next, create a StatefulWidget called MyHomePage, using the stful shortcut. It will generate the following code:
class MyHomePage extends StatefulWidget { 
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Container(  );  
}
}

  1. In the _MyHomePageState class, create a few fields. The first one is an instance of the BooksHelper class, then one for the List of books that will be shown on the screen, an integer for the number of books retrieved, and a TextEditingController for the search text field, as shown in the following code block:
 BooksHelper helper; 
 List<dynamic> books = List<dynamic>();
 int booksCount;
 TextEditingController txtSearchController;
  1. When this screen loads, we want to set the BooksHelper instance and the txtSearchController object, and then retrieve a List of books. For this last action, we'll create a new method called initialize(), as follows:
@override 
  void initState() {
    helper = BooksHelper();
    txtSearchController = TextEditingController();
    initialize();
    super.initState();
  }

The initialize() method will be asynchronous and will return a Future. Inside the method, we'll call the getBooks() method from our BooksHelper.

For this example, we'll just retrieve the books containing "Flutter". In a real-world app, you would probably choose a smoother first screen, maybe guiding to a new search, but, for this project, this is totally adequate.

  1. After retrieving the books, call the setState() method to update the books list and the booksCount fields. Add the following code at the end of the _MyHomePageState class:
Future initialize() async { 
    books = await helper.getBooks('Flutter');
    setState(() {
      booksCount = books.length;
      books = books;
    });
  }

Next, we'll update the build() method, and here, we'll add the first piece of code that will help us build a responsive app.

  1. In the build() method, create a Boolean called isSmall and set it to false, as follows:
bool isSmall = false; 

We'll consider a "small" screen to be every screen that has a width of less than 600 units. In order to retrieve the screen size, we'll use the MediaQuery widget.

Flutter measures size with "logical pixels", which are basically the same as device-independent pixels (dips) for Android. This allows your apps to look roughly the same size on every device. For more information on the way logical pixels relate to physical pixels, have a look at the following page: https://api.flutter.dev/flutter/dart-ui/Window/devicePixelRatio.html.
  1. Add the code to check whether the device is "small", as follows:
if (MediaQuery.of(context).size.width < 600) { 
      isSmall = true;
    }

As usual, we'll return a Scaffold here. The Scaffold will contain an AppBar, with the title of "My Books". Let's also add an actions array. From the actions of our user, we'll be able to change the page, and here, we'll add the first responsive widgets for this app.

  1. Add the following code to return a Scaffold in the build() method:
return Scaffold( 
        appBar: AppBar(
          title: Text('My Books'),
          actions: <Widget>[]
) );

In the actions of the AppBar, we'll add two InkWell widgets, which are simply rectangular areas that respond to touch (or click on desktops).

  1. For the first InkWell widget child, add a Padding, with a padding of 20 on every side. The child of the Padding widget will depend on the size of the screen. On smaller screens, the user will see the home icon from the Icons enumerator. For larger screens, they will see a text with Home, as follows:
InkWell( 
child: Padding(
padding: EdgeInsets.all(20.0),
child: (isSmall) ? Icon(Icons.home) : Text('Home')),
),
  1. The second InkWell will follow the same logic, but instead of showing the home icon, we'll show the star icon, and the text will be 'Favorites', as shown in the following code block:
InkWell( 
child: Padding(
padding: EdgeInsets.all(20.0),
child: (isSmall) ? Icon(Icons.star) : Text('Favorites')),           
),
  1. In the body of the Scaffold, place a SingleChildScrollView to prevent the content of the screen overflowing the available space. Its child will be a Column, as shown in the following code snippet:
body: SingleChildScrollView( 
child: Column(children: [ ]),
  1. The first widget in the Column is a Padding so that the small form allowing the user to search for a book will have 20 logical pixels of space in all directions. The child of the Padding will be a Row, as shown in the following code snippet:
Padding( 
padding: EdgeInsets.all(20),
child: Row(children: []),
)
  1. In the Row, put a Text containing a 'Search book' string, as follows:
Text('Search book'), 
  1. Still in the Row, add a Container with the same 20 logical pixels padding and a width of 200. Its child will be a TextField, as shown in the following code snippet:
Container( 
padding: EdgeInsets.all(20),
width: 200,
child: TextField()
)
  1. Now, we need to set the TextField. Its controller will be the txtSearchController that we created at the top of the class. For mobile devices that have a virtual keyboard, the keyboardType will be of type text, and the textInputAction will be of type search, as shown in the following code snippet:
controller: txtSearchController, 
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,

Still, only for virtual keyboards, we want to submit the search query when the user clicks on the search button.

  1. Add an onSubmitted() method that will call the helper getBooks() asynchronous method, and when the value of the query returns, call the setState() method to update the books List, as follows:
onSubmitted: (text) { 
helper.getBooks(text).then((value) {
setState(() {
books = value;
});
});
},
  1. The last widget in the Row will be a search icon button, necessary for all devices that have no virtual keyboards, but visible in all devices. Enclose it into another Padding of 20, like this:
Container( 
padding: EdgeInsets.all(20),
child: IconButton(
icon: Icon(Icons.search),
onPressed: () =>
helper.getBooks(txtSearchController.text)
)),
  1. Now, the Row contains the Search Text, TextField, and IconButton. Under this Row, we'll need to place the actual result of the query. For now, just add a Padding in the column, with a child that will be an empty Container, as follows:
Padding( 
padding: EdgeInsets.all(20),
child: Container(),
),

The child of the padding should contain the list of Books that we retrieved using the helper.getBooks() method. And that's exactly what we'll do in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.58.194