Creating an Android app in Flutter: Cinema+

10 42
Avatar for Infinity
3 years ago

Today I want to show you the Cinema+ app, which I am developing for Android with the Flutter SDK, belonging to Google. The application arises as a hobby and as a conclusion to all the courses that I had seen on the internet related to this topic of mobile development with Flutter. For those of you who don't know, I'm going to give you a brief introduction to what Flutter is.

Flutter and Dart:

Flutter is an open source SDK, with which you can create mobile applications. It was launched by Google in May 2017. It is mainly used for applications in Android, IOS and Web development. It is written in C, C ++ and Dart. It uses Dart as a programming language, which was also created by Google. Far from being a disadvantage because it is a language that nobody knew (at that time), the idea that both had been developed by the same company, was an advantage in itself. The biggest advantage that I see to Dart is that it allows an Ahead Of Time (AOT) that allows us a fast and predictable compilation to a native code, in addition to the Just in Time (JIT) that allows the compilation to be at an exceptional speed development, with the popular Hot Reload. We programmers know that time is money, and compiling software usually takes valuable seconds and sometimes minutes, which means that when all those times are added, it is precisely that, a huge loss in just compilation. Dart also makes the creation of animations and transitions much easier, running at 60 fps. It is compiled in native code and is very easy to learn, with a very fast learning curve. Also, if Google after evaluating a dozen languages ​​chose Dart, they must not be so wrong, after all it is Google who we are talking about here.

The development IDE I am working on is Visual Studio Code, well known by all programmers these days, for the facilities it offers and its multiple customizations to improve development in general.

Well, let's go to the application itself.

Cinema+

The idea is to make an application where you can see movies, series and actors. In movies, those that are currently in theaters, the next releases, the movies by genre, the popular ones and the movies with the highest ranking. In the series, those that will come out today by programming, those that are active and the same philosophy as the movies later. As for the actors, the most popular and those who are trending this week.

It will be selected as favorites if the user so wishes. This leads to showing movies, series and actors on a page of favorites, for easy location later. It also has a search implemented, making it easier for the user to search the immense online database.

In addition, the database is in sqlite, where all the records that are viewed by the user are stored. This makes it possible that in case there is no internet connection, the user has the experience of being able to observe the last records that he had previously seen in the app, without having to be connected.

The photos are saved locally, using a plugin that allows the download of the photos to the memory of the cell phone. It always tries to find the photo locally first, if it can't find it it tries to download it from the internet and then stores it for when it is later asked to show it locally. This ensures that there is no unnecessary consumption of the mobile data plan.

Beginning

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  FileStorage.fs.appDocsDir;

  int theme = await DBProvider.db.getTheme();
  bool desing_detail = await DBProvider.db.getDesingDetail();

  return runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => new MovieProvider()),
        ChangeNotifierProvider(create: (_) => new SerieProvider()),
        ChangeNotifierProvider(create: (_) => new ActorProvider()),
        ChangeNotifierProvider(
            create: (_) => new ThemeChanger(theme, desing_detail)),
      ],
      child: MyApp(),
    ),
  );
}

You can see the start of the file system and the database, in order to store the database if it is not already on the cell phone, make a copy of it, also to save the photos. It also has 4 classes that extend from Provider, which is a state manager, to manage the states of the application.

The data is taken from the internet, from the page themoviedb. An extensive database which, through its API, can be surveyed on everything related to the world of cinema.

Example of the movie data model, which is returned by the themoviedb API:

class Movies {
  List<Movie> items = [];
  Movies();

  Movies.fromJsonList(List<dynamic> jsonList) {
    if (jsonList == null) return;

    for (var item in jsonList) {
      final movie = new Movie.fromJson(item);
      items.add(movie);
    }
  }
}

class Movie {
  Movie({
    this.adult,
    this.backdropPath,
    this.genreIds,
    this.id,
    this.originalLanguage,
    this.originalTitle,
    this.overview,
    this.popularity,
    this.posterPath,
    this.releaseDate,
    this.title,
    this.video,
    this.voteAverage,
    this.voteCount,
    this.favorit = false,
    this.uniqueID,
    this.belongsToCollection,
    this.budget,
    this.generos,
    this.homepage,
    this.imdbId,
    this.productionCompanies,
    this.productionCountries,
    this.revenue,
    this.runtime,
    this.spokenLanguages,
    this.status,
    this.tagline,
  });

  String uniqueID;
  bool favorit;
  bool adult;
  String backdropPath;
  List<int> genreIds;
  int id;
  String originalLanguage;
  String originalTitle;
  String overview;
  double popularity;
  String posterPath;
  String releaseDate;
  String title;
  bool video;
  double voteAverage;
  int voteCount;

  dynamic belongsToCollection;
  int budget;
  List<Generos> generos;
  String homepage;

  String imdbId;

  List<ProductionCompany> productionCompanies;
  List<ProductionCountry> productionCountries;

  int revenue;
  int runtime;
  List<SpokenLanguage> spokenLanguages;
  String status;
  String tagline;

  factory Movie.fromJson(Map<String, dynamic> json) => Movie(
        backdropPath: json["backdrop_path"],
        adult: json["adult"],
        genreIds: List<int>.from(json["genre_ids"].map((x) => x)),
        id: json["id"],
        //originalLanguage: originalLanguageValues.map[json["original_language"]],
        originalTitle: json["original_title"],
        overview: json["overview"],
        popularity: json["popularity"].toDouble(),
        posterPath: json["poster_path"],
        releaseDate: json["release_date"],
        title: json["title"],
        video: json["video"],
        voteAverage: json["vote_average"].toDouble(),
        voteCount: json["vote_count"],
      );
…

In order to access the API, we have to have a Key, it is provided by the API after you register on the page.

class Global {
  String _apikey = '64520a5c6b745d56b5ecf3af54cb55a88';
  String _url = 'api.themoviedb.org';
  String _language = 'es-ES';

  String get apikey => _apikey;
  String get url => _url;
  String get language => _language;
}

We load the movies that are in theaters at the Cinema:

Future<List<Movie>> loadMoviesNowPlaying() async {
    if (_isLoadNowPlaying) return [];
    _isLoadNowPlaying = true;
    _nowPlayingPage++;

    final url = Uri.https(api.url, '3/movie/now_playing', {
      'api_key': api.apikey,
      'language': api.language,
      'page': _nowPlayingPage.toString(),
    });

    final resp =
        await _procesarRespuesta(url, "movie_now_playing", _nowPlayingPage, 0);

    resp.sort((a, b) => b.voteAverage.compareTo(a.voteAverage));

    _moviesNowPlaying.addAll(resp);

    notifyListeners();

    _isLoadNowPlaying = false;

    return _moviesNowPlaying;
  }

Future<List<Movie>> _procesarRespuesta(
      Uri url, String modo, int page, int idGenre) async {
    List<Movie> respuesta;

    bool isOnline = await ConnectionStatus.cm.online;

    if (isOnline) {
      final resp = await http.get(url);

      dynamic decodeData = json.decode(resp.body);

      Movies respuesta_temp;
      if (modo != "by_actor")
        respuesta_temp = new Movies.fromJsonList(decodeData['results']);
      else
        respuesta_temp = new Movies.fromJsonList(decodeData['cast']);

      List<int> favoritMovies = await DBProvider.db.getFavoritMovies();

      for (var i = 0; i < respuesta_temp.items.length; i++) {
        await DBProvider.db
            .addMovie(respuesta_temp.items[i], page, modo, idGenre);

        int index = favoritMovies.indexOf(respuesta_temp.items[i].id);
        if (index != -1) {
          respuesta_temp.items[i].favorit = true;
        }
      }

      respuesta = respuesta_temp.items;
    } else {

      respuesta = await DBProvider.db.getMovies(page, modo, idGenre);
    }

    return respuesta;
  }

We then have the list of movies that we want to represent visually in the app. Until then, the code, being Dart, has been practically known, due to its similarity to C ++. The visual part of the application, if it is completely new. Everything works based on Widgets.

class mySliderTop extends StatefulWidget {
  final String tipo;
  const mySliderTop({Key key, @required this.tipo}) : super(key: key);

  @override
  _SliderTopState createState() => _SliderTopState(tipo);
}

class _SliderTopState extends State<mySliderTop> {
  final String tipo;
  _SliderTopState(this.tipo);

  PageController _pageController = PageController(
    viewportFraction: 1,
    keepPage: true,
  );

  @override
  void dispose() {
    _pageController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    dynamic movieProvider;
    dynamic serieProvider;
    dynamic actorProvider;

    List<Movie> movies = [];
    List<Serie> series = [];
    List<Actor> actors = [];

    if (this.tipo == "Movie") {
      movieProvider = Provider.of<MovieProvider>(context);
      movies = movieProvider.moviesNowPlaying;
    } else if (this.tipo == "Serie") {
      serieProvider = Provider.of<SerieProvider>(context);
      series = serieProvider.tvAirToday;
    } else if (this.tipo == "Actor") {
      actorProvider = Provider.of<ActorProvider>(context);
      actors = actorProvider.popularActors;
    }

    _pageController.addListener(() {
      if (_pageController.position.pixels >=
          _pageController.position.maxScrollExtent - 200) {
        if (this.tipo == "Movie") {
          if (!movieProvider.isLoadingPlaying())
            movieProvider.loadMoviesNowPlaying();
        }

        if (this.tipo == "Serie") {
          if (!serieProvider.isLoadingTVAirToday())
            serieProvider.loadTVAirToday();
        }

        if (this.tipo == "Serie") {
          if (!actorProvider.isLoadingPopularActors())
            actorProvider.loadPopularActors();
        }
      }
    });

    if (movies.length != 0)
      return _buildHomeWidget(movies, movieProvider, tipo, "NowPlaying");
    if (series.length != 0)
      return _buildHomeWidget(series, serieProvider, tipo, "TVAirToday");
    if (actors.length != 0)
      return _buildHomeWidget(actors, actorProvider, tipo, "PopularActor");
    else {
      return _buildLoadingWidget();
    }
  }

Widget _buildHomeWidget(
      List<dynamic> data, dynamic dataDBProvider, String tipo, String subtipo) {
    final appTheme = Provider.of<ThemeChanger>(context);
    if (data.length != 0) {
      return Container(
        height: 420.0,
        child: PageIndicatorContainer(
          align: IndicatorAlign.bottom,
          length: data.length,
          indicatorSpace: 8.0,
          padding: const EdgeInsets.all(15.0),
          indicatorColor: Style.MyColors.titleColor,
          indicatorSelectorColor: Style.MyColors.secondColor,
          shape: IndicatorShape.circle(size: 5.0),
          child: PageView.builder(
            controller: _pageController,
            scrollDirection: Axis.horizontal,
            itemCount: data.length,
            onPageChanged: (index) {
              //_updatePalette(index);
            },
            itemBuilder: (context, index) {
              data[index].uniqueID = '${data[index].id}-$tipo-$subtipo';

              return Hero(
                tag: data[index].uniqueID,
                child: GestureDetector(
                  onTap: () {
                    if (tipo == "Movie")
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => (appTheme.desing)
                              ? HomeSliver(
                                  movie: data[index],
                                  tipo: "Movie",
                                )
                              : PeliculaDetailScreen(movie: data[index]),
                        ),
                      );

                    if (tipo == "Serie")
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => (appTheme.desing)
                              ? HomeSliver(
                                  serie: data[index],
                                  tipo: "Serie",
                                )
                              : SerieDetailScreen(serie: data[index]),
                        ),
                      );

                    if (tipo == "Actor")
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) =>
                              ActorDetailScreen(actor: data[index]),
                        ),
                      );
                  },
…

The result is as follows:

Movies main screen
Movies main screen
Movie detail screen
Actor screen
Favorite screen

To end

These are just a few samples of the app screens. It is still under development to incorporate new features, and make it more attractive to users. Maybe later I even include cryptocurrencies in some way, obviously BCH. What do you think of?

Do you like how the application is looking?

Credits:

100% original content.

All images are from the app in development. The images of movies, series and actors that are in it, correspond to each one.

Sponsors of Infinity
empty
empty
empty

4
$ 0.07
$ 0.05 from @Peter-Molnar
$ 0.02 from @Niazi420
Sponsors of Infinity
empty
empty
empty
Avatar for Infinity
3 years ago

Comments

This is a fantastic app and looking absolutely amazing. Well done! I started to learn coding, but I found it so difficult. I started with solidity as I thought I learn how to code using solidity to become a blockchain developer or at least to create a simple dApp. It is not simple at all.

$ 0.00
3 years ago

Thanks friend. I am very glad that you liked the app. Do not be discouraged. Today you can learn what you propose, everything is on the internet. It's difficult but not impossible. Anyway, if you have doubts you can ask, I will gladly help you. Thanks for stopping by!

$ 0.00
3 years ago

You are welcome. You can call me Peter. I agree there are lots of resources and learning materials online some of them are free which is good for basics or not even basics.

$ 0.00
3 years ago

You're right. Everything remains to depend on yourself, and the time you dedicate. When you start and things start to work is when you get most excited. The time will come when you will write lines of code as one more article. 🙂

$ 0.00
3 years ago

Best work I have ever seen. Well! I am still in a learning age. I am learning c++ but now my main focus is on the databases. I think you should make a payment method option through BCH. Btw, Thank you for this. It will be helpful for my projects.

$ 0.00
3 years ago

Thanks friend for stopping by. You are the first to comment, and very few visits that this article has. I guess many don't like apps and movies lol. Nah, let's not get serious. I am a computer engineer. A 5-year college degree. I am glad that what is stated in the article and the application in general are useful to you. Thanks for the advice. I will keep it in mind. Anything you need you tell me. Greetings.

$ 0.02
3 years ago

Actually, when I start writing I wrote two to three articles regarding programming etc. But I found that people didn't show any interest at that time so that’s why I left those topics. People like interesting topics like funny where they can easy to interact with others. No worries bruh, don't stop writing just keep going on and one day everyone read your articles. You are doing good enough. Best of luck.

$ 0.00
3 years ago

That's what I try my friend. I try to write my best. But if you are right. I've been here in read for a week and I'm learning from everyone. What is the content that catches the most. Rusty visited me, and perhaps by doing it very early, it caught my attention, now it has been more than 4 days without visiting my articles. I confess that I have felt a little sad, but the reality is that I am not going to write thinking if a robot likes me or not, that it would not be bad if it were, but my content is for you, and it satisfies me more when it finds readers like you that what they are seeing is useful. I will continue writing. Slowly. Life nothing gets gifted. All success comes from a great effort and prior commitment. Again, Thank you very much for reading and supporting me.

$ 0.01
3 years ago

Love your dedication. Indeed, hard work pays off. Just keep going. My best wishes are with you.

$ 0.00
3 years ago

THANKS!

$ 0.00
3 years ago