Flutter Basics for Beginners (Part VII)

Most mobile applications contain various pictures.





But what about without them? Pictures make the user interface richer and clearer.





Flutter has built-in support for images. The most commonly used class is the Image class, which we will look at in this article.





Well let's go!





Our plan
  • Part 1  - introduction to development, first appendix, concept of state;





  • Part 2  - pubspec.yaml file and using flutter on the command line;





  • Part 3  - BottomNavigationBar and Navigator;





  • Part 4  - MVC. We will use this particular pattern as one of the simplest;





  • Part 5  - http package. Creation of the Repository class, first requests, listing of posts;





  • Part 6 - working with forms, text fields and creating a post.





  • Part 7 (current article) - working with pictures, displaying pictures in the form of a grid, receiving pictures from the network, adding your own to the application;





  • Part 8 - creating your own theme, adding custom fonts and animations;





  • Part 9 - a little about testing;





Adding pictures to the project

First, let's try to add our own pictures to the project.





Be careful: images added to your project increase the size of the application, so don't overdo it!





To add pictures, we need to create a new directory images



at the root of the project:





images



pubspec.yaml:





#  
dependencies:
  flutter:
    sdk: flutter

  # ...

#   
dev_dependencies:
	
	# ...

#         assets 
flutter:

  # ,    MaterialApp   
  #   Material Design
  uses-material-design: true

  #   images
  # /   ,    
  #       images
  assets:
    - images/
      
      



. Github':





AlbumListPage



:






import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';

class AlbumListPage extends StatefulWidget {
  @override
  _AlbumListPageState createState() => _AlbumListPageState();
}
class _AlbumListPageState extends State<AlbumListPage> {
  //    
  final fileImages = [
    "applejack.png",
    "fluttershy.png",
    "rarity.png",
    "starlight_glimmer.png",
    "twillight_sparkle.png"
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Album List Page"),
        ),
        body: _buildContent()
    );
  }
  
  //      
  Widget _buildContent() {
    //      flutter_staggered_grid_view
    //   StaggeredGridView
    return StaggeredGridView.countBuilder(
      //  
      itemCount: fileImages.length,
      // crossAxisCount   
      //     
      crossAxisCount: 8,
      //   
      mainAxisSpacing: 10,
      //   
      crossAxisSpacing: 10,
      staggeredTileBuilder: (index) {
        //    4    ( )
        //       2   ( )
        return StaggeredTile.count(4, index % 2 == 0 ? 4 : 8);
      },
      //  
      itemBuilder: (context, index) {
        return Container(
          decoration: BoxDecoration(
            border: Border.all(color: Colors.pinkAccent, width: 1)
          ),
          // Image.asset   
          //      pubspec.yaml
          //  asset  Image   
          child: Image.asset("images/${fileImages[index]}"),
        );
      },
);

  }
}
      
      



.





! .





REST API .





, AlbumListPage. , , .







.





Photo



:





//     Post
class Photo {
  final int _id;
  final String _title;
  final String _url;
  
  Photo.fromJson(Map<String, dynamic> json) :
      _id = json["id"],
      _title = json["title"],
      _url = json["url"];
}

class PhotoList {
  final List<Photo> photos = [];
  
  PhotoList.fromJson(List<dynamic> jsonItems) {
    for (var jsonItem in jsonItems) {
      photos.add(Photo.fromJson(jsonItem));
    }
  }
  
}

abstract class PhotoResult {}

class PhotoResultSuccess extends PhotoResult {
  final PhotoList photoList;
  PhotoResultSuccess(this.photoList);
}

//  
class PhotoResultFailure extends PhotoResult {
  final String error;
  PhotoResultFailure(this.error);
}

//  
class PhotoResultLoading extends PhotoResult {
  PhotoResultLoading();
}
      
      



Repository



:





Future<PhotoList> fetchPhotos() async {
  //   URL,  
  //    
  final url = Uri.parse("$SERVER/photos");
  //  GET 
  final response = await http.get(url);

  //   
  if (response.statusCode == 200) {
    //       
    // json.decode  
    return PhotoList.fromJson(json.decode(response.body));
  } else {
    //     
    throw Exception("failed request");
  }
}
      
      



AlbumController



:





// AlbumController    PostController
class AlbumController extends ControllerMVC {
  final Repository repo = Repository();
  
  //  
  PhotoResult currentState = PhotoResultLoading();
  
  void init() async {
    try {
      //  
      final photoList = await repo.fetchPhotos();
      // 
      setState(() => currentState = PhotoResultSuccess(photoList));
    } catch (error) {
      //  
      setState(() => currentState = PhotoResultFailure(" "));
    }
  }
  
}
      
      



AlbumListPage



:





class AlbumListPage extends StatefulWidget {
  @override
  _AlbumListPageState createState() => _AlbumListPageState();
}

class _AlbumListPageState extends StateMVC {

  //   
  // late    
  late AlbumController _controller;

  _AlbumListPageState() : super(AlbumController()){
    _controller = controller as AlbumController;
  }

  @override
  void initState() {
    super.initState();
    //    JSONPlaceholder
    _controller.init();
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Album List Page"),
        ),
        body: _buildContent()
    );
  }

  Widget _buildContent() {
    //   
    final state = _controller.currentState;
    if (state is PhotoResultLoading) {
      // 
      return Center(
        child: CircularProgressIndicator(),
      );
    } else if (state is PhotoResultFailure) {
      // 
      return Center(
        child: Text(
            state.error,
            textAlign: TextAlign.center,
            style: Theme.of(context).textTheme.headline4!.copyWith(color: Colors.red)
        ),
      );
    } else {
      final images = (state as PhotoResultSuccess).photoList.photos;
      //   StaggeredGridView  
      //    
      return StaggeredGridView.countBuilder(
        //  
        itemCount: images.length,
        // crossAxisCount   
        //     
        crossAxisCount: 8,
        //   
        mainAxisSpacing: 10,
        //   
        crossAxisSpacing: 10,
        staggeredTileBuilder: (index) {
          //      4  ( )
          //       2   ( )
          return StaggeredTile.count(4, index % 2 == 0 ? 4 : 8);
        },
        //  
        itemBuilder: (context, index) {
          return Container(
            decoration: BoxDecoration(
                border: Border.all(color: Colors.pinkAccent, width: 1)
            ),
            //    Image.network 
            //    
            child:  Image.network(
              images[index].url,
              //     
              width: double.infinity,
              height: double.infinity,
              //   
              fit: BoxFit.cover,
              //    
              //    Loading...
              loadingBuilder: (context, widget, imageChunkEvent) {
                if (imageChunkEvent == null) {
                  return widget;
                }
                return Center(child: Text("Loading..."));
              },
              //    
              //     Error!
              errorBuilder: (context, obj, stacktrace) => Center(child: Text("Error!")),
            ),
          );
        },

      );
    }
  }
}
      
      



.





!





, Flutter .





Image.network



is not a panacea and therefore it is better to use special libraries with more functionality in combat projects.





One of these promising libraries is cached_network_image





This is a fairly simple library that takes care of all the technical issues and complexities.





Useful links:





  • Link to Github





  • Image class





  • Adding assets and images





Good code everyone!








All Articles