Cat App, A Flutter Mini Project

Flutter Cats App

A simple flutter application to share your cat pictures.

Features

  • Personalized Profile Name, Picture and Bio.
  • Gallery to view the Cat Pictures.
  • Like and Unlike the Cat Picture.

Dependencies

  • pubspec.yml
name: cat_api
description: A new Flutter project.


version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  http: ^0.13.3
  bloc: ^7.0.0
  flutter_bloc: ^7.0.1
  lazy_load_scrollview: ^1.3.0
  http_auth: ^1.0.0
  flutter_spinkit: ^5.0.0
  like_button: ^2.0.2
  cached_network_image: ^3.0.0
  firebase_core: ^1.2.1
  flutter_signin_button: ^2.0.0
  firebase_auth: ^1.3.0
  flutter_facebook_auth: ^3.4.1
  google_sign_in: ^5.0.4
  shared_preferences: ^2.0.6


dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_native_splash: ^1.1.8+4

flutter_native_splash:
  color: "#ffffff"
  color_dark: "#1a1a1a"
  image: assets/cat.png
  image_dark: assets/cat.png
  andriod: true
  ios: true

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  assets:
    - assets/images/placeholder.png

Source Code

  • Create a flutter app using flutter create Cat App.
  • Go inside the Cat App folder and inside the lib folder add the following folder, files and .dart files.
  • lib/main.dart
import './bloc/bloc_authentification/bloc.dart';
import './bloc/cat_facts/bloc.dart';
import './bloc/cat_images/bloc.dart';
import './bloc/cat_like/bloc.dart';
import './features/home/home_1.dart';
import './features/home/home_2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:firebase_core/firebase_core.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: _initialization,
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return MaterialApp(home: Text('Error connecting to firebase'));
          } else if (snapshot.connectionState == ConnectionState.done) {
            return MultiBlocProvider(
              providers: [
                BlocProvider<CatsBloc>(
                    create: (context) => CatsBloc()..add(InitialCats())),
                BlocProvider<CatFactsBloc>(
                    create: (context) => CatFactsBloc()..add(FactsLoaded())),
                BlocProvider<LikeBloc>(create: (context) => LikeBloc()),
                BlocProvider<AuthBloc>(create: (context) => AuthBloc()),
              ],
              child: BlocBuilder<AuthBloc, AuthState>(
                  buildWhen: (previous, current) {
                return true;
              }, builder: (context, state) {
                if (state is AuthSuccess) {
                  if (state.currentUser != null) {
                    return MaterialApp(
                      debugShowCheckedModeBanner: false,
                      title: 'Cats App',
                      home: HomeScreenUser(),
                    );
                  } else {
                    return Login();
                  }
                } else {
                  return Login();
                }
              }),
            );
          } else {
            return CircularProgressIndicator();
          }
        });
  }
}
  • lib/bloc/bloc_authentification/bloc.dart
import 'dart:async';

import 'package:cat_api/utils/auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:meta/meta.dart';

import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';

part 'event.dart';
part 'state.dart';

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final authService = AuthService();
  final fb = FacebookAuth.instance;
  final google = GoogleSignIn(
      scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly']);

  AuthBloc()
      : super(AuthService().user != null
            ? AuthSuccess(currentUser: AuthService().user)
            : NoAuth());

  @override
  Stream<AuthState> mapEventToState(
    AuthEvent event,
  ) async* {
    if (event is FacebookLoginEvent) {
      final res =
          await fb.login(permissions: ['public_profile', 'user_photos']);

      if (res.status == LoginStatus.success) {
        final credentials =
            FacebookAuthProvider.credential(res.accessToken!.token);
        final user = await authService.signInWithCredentials(credentials);

        yield AuthSuccess(currentUser: user.user);
      } else {
        yield (NoAuth());
      }
    } else if (event is LogOutEvent) {
      await fb.logOut();
      await google.signOut();
      yield NoAuth();
    } else if (event is GoogleLoginEvent) {
      try {
        final googleUser = await google.signIn();
        final googleAuth = await googleUser?.authentication;
        final credential = GoogleAuthProvider.credential(
            idToken: googleAuth?.idToken, accessToken: googleAuth?.accessToken);
        final user = await authService.signInWithCredentials(credential);

        yield AuthSuccess(currentUser: user.user);
      } catch (error) {}
    }
  }
}
  • lib/bloc/bloc_authentification/event.dart
part of 'bloc.dart';

@immutable
abstract class AuthEvent {}

class FacebookLoginEvent extends AuthEvent {}

class GoogleLoginEvent extends AuthEvent {}

class LogOutEvent extends AuthEvent {}
  • lib/bloc/bloc_authentification/state.dart
part of 'bloc.dart';

@immutable
abstract class AuthState {
  final User? currentUser = null;
}

class AuthSuccess extends AuthState {
  final User? currentUser;

  AuthSuccess({required this.currentUser});
}

class NoAuth extends AuthState {
  final User? currentUser = null;
}
  • lib/bloc/cat_facts/bloc.dart
import 'dart:async';
import 'dart:convert';

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
import 'package:http/http.dart' as http;

part 'event.dart';
part 'state.dart';

class CatFactsBloc extends Bloc<CatFactsEvent, CatFactsState> {
  CatFactsBloc() : super(CatFactsInitial());

  @override
  Stream<CatFactsState> mapEventToState(
    CatFactsEvent event,
  ) async* {
    if (event is FactsLoaded) {
      final facts = await loadFacts();
      yield CatsFactsLoaded(facts: facts);
    }
  }
}

Future<http.Response> getData(String uri, Map<String, String> headers) async {
  return await http.get(Uri.parse(uri));
}

Future<List<String>> loadFacts() async {
  var response = await getData('https://catfact.ninja/facts?limit=3', {});
  if (response.statusCode == 200) {
    List<dynamic> data = json.decode(response.body)['data'];
    return data.length > 0
        ? data.map((i) => i['fact'].toString()).toList()
        : [];
  } else {
    return [];
  }
}
  • lib/bloc/cat_facts/event.dart
part of 'bloc.dart';

@immutable
abstract class CatFactsEvent {}

class FactsLoaded extends CatFactsEvent {}
  • lib/bloc/cat_facts/state.dart
part of 'bloc.dart';

@immutable
abstract class CatFactsState {}

class CatFactsInitial extends CatFactsState {}

class CatsFactsLoaded extends CatFactsState {
  final List<String> facts;

  CatsFactsLoaded({required this.facts});
}
  • lib/bloc/cat_images/bloc.dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

part 'event.dart';
part 'state.dart';

class CatsBloc extends Bloc<CatsEvent, CatsState> {
  final List<ImagesData> _images = [];
  CatsBloc() : super(CatsInitial());

  @override
  Stream<CatsState> mapEventToState(
    CatsEvent event,
  ) async* {
    if (event is InitialCats) {
      final catsImages = await loadImages();
      _images..addAll(catsImages);
      yield CatsLoaded(catsImages: _images);
    } else if (event is LoadNewImages) {
      final catsImages = await loadImages();
      _images..addAll(catsImages);
      yield CatsLoaded(catsImages: _images);
    }
  }
}

Future<http.Response> getData(String uri, Map<String, String> headers) async {
  return await http.get(Uri.parse(uri), headers: headers);
}

const CACHED_IMAGES = 'CACHED_IMAGES';

Future<List<ImagesData>> loadImages() async {
  final sharedPreference = await SharedPreferences.getInstance();
  try {
    var response = await getData(
        'https://api.thecatapi.com/v1/images/search?limit=20',
        {'x-api-key': 'd12e1ec7-64e0-4026-bfa0-f8336b070970'});

    if (response.statusCode == 200) {
      List<dynamic> data = json.decode(response.body);
      List<ImagesData> list = data.length > 0
          ? data
              .map((e) =>
                  ImagesData(url: e['url'].toString(), id: e['id'].toString()))
              .toList()
          : [];

      sharedPreference.setStringList(
          CACHED_IMAGES,
          data
              .map(
                (e) => json.encode({'url': e['url'], 'id': e['id']}),
              )
              .toList());

      return list;
    } else {
      return [];
    }
  } on SocketException {
    final cachedData = sharedPreference.getStringList(CACHED_IMAGES);
    if (cachedData == null) {
      return [];
    }

    return Future.value(cachedData.map((e) {
      final decodedImage = json.decode(e);
      return ImagesData(url: decodedImage['url'], id: decodedImage['id']);
    }).toList());
  } catch (error) {
    return [];
  }
}

class ImagesData {
  String url;
  String id;

  ImagesData({
    required this.url,
    required this.id,
  });
}
  • lib/bloc/cat_images/event.dart
part of 'bloc.dart';

@immutable
abstract class CatsEvent {}

class InitialCats extends CatsEvent {}

class LoadNewImages extends CatsEvent {}
  • lib/bloc/cat_images/state.dart
part of 'bloc.dart';

@immutable
abstract class CatsState {}

class CatsInitial extends CatsState {}

class CatsLoaded extends CatsState {
  final List<ImagesData> catsImages;

  CatsLoaded({required this.catsImages});
}
  • lib/bloc/cat_like/bloc.dart
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';

part 'event.dart';
part 'state.dart';

class LikeBloc extends Bloc<LikeEvent, LikesState> {
  final Set<String> _likes = {};
  LikeBloc() : super(Likes(likes: {}));

  @override
  Stream<LikesState> mapEventToState(
    LikeEvent event,
  ) async* {
    if (event is Like) {
      _likes.add(event.id);
      yield Likes(likes: _likes);
    } else if (event is Dislike) {
      _likes.remove(event.id);
      yield Likes(likes: _likes);
    }
  }
}
  • lib/bloc/cat_like/event.dart
part of 'bloc.dart';

@immutable
abstract class LikeEvent {}

class Like extends LikeEvent {
  final String id;

  Like({required this.id});
}

class Dislike extends LikeEvent {
  final String id;

  Dislike({required this.id});
}
  • lib/bloc/cat_like/state.dart
part of 'bloc.dart';

@immutable
abstract class LikesState {}

class Likes extends LikesState {
  final Set<String> likes;

  Likes({required this.likes});
}
  • lib/cached/cached_network_images.dart
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';

class CachedImage extends StatelessWidget {
  final String imageUrl;

  const CachedImage({Key? key, required this.imageUrl}) : super(key: key);

  Widget _imageWidget(ImageProvider imageProvider) {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(8)),
        image: DecorationImage(
          image: imageProvider,
          fit: BoxFit.cover,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
        imageUrl: imageUrl,
        imageBuilder: (context, imageProvider) => _imageWidget(imageProvider),
        placeholder: (context, url) =>
            Center(child: CircularProgressIndicator()),
        errorWidget: (context, url, error) =>
            _imageWidget(AssetImage('assets/images/placeholder.png')));
  }
}
  • lib/features/cats/cats_screen.dart
import '/bloc/cat_like/bloc.dart';
import '/features/cats/detail_page.dart';
import '/cached/cached_network_images.dart';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '/bloc/cat_images/bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
import 'package:like_button/like_button.dart';

class CatsScreen extends StatelessWidget {
  const CatsScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    LikeBloc _likeBloc = BlocProvider.of<LikeBloc>(context);
    CatsBloc _catsBloc = BlocProvider.of<CatsBloc>(context);
    return BlocBuilder<CatsBloc, CatsState>(
        buildWhen: (previous, current) => previous != current,
        builder: (context, state) {
          if (state is CatsLoaded) {
            return LazyLoadScrollView(
              onEndOfPage: () {
                _catsBloc.add(LoadNewImages());
              },
              child: GridView.builder(
                  padding: EdgeInsets.all(3),
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3,
                    mainAxisSpacing: 1,
                    crossAxisSpacing: 1,
                    childAspectRatio: 1,
                  ),
                  itemCount: state.catsImages.length,
                  itemBuilder: (context, index) {
                    return InkWell(
                      onTap: () {
                        Navigator.of(context).push(MaterialPageRoute<void>(
                            builder: (BuildContext context) {
                          return HeroScreen(
                            imgUrl: '${state.catsImages[index].url}',
                            tag: 'dash${state.catsImages[index].id}',
                          );
                        }));
                      },
                      child: Hero(
                        tag: 'dash${state.catsImages[index].id}',
                        flightShuttleBuilder: (
                          BuildContext flightContext,
                          Animation<double> animation,
                          HeroFlightDirection flightDirection,
                          BuildContext fromHeroContext,
                          BuildContext toHeroContext,
                        ) {
                          return SingleChildScrollView(
                            child: Container(
                              margin: EdgeInsets.all(30),
                              child: Image.network(
                                  '${state.catsImages[index].url}'),
                            ),
                          );
                        },
                        child: Stack(
                          children: [
                            CachedImage(imageUrl: state.catsImages[index].url),
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.end,
                                children: [
                                  Row(
                                    mainAxisAlignment: MainAxisAlignment.end,
                                    children: [
                                      BlocBuilder<LikeBloc, LikesState>(
                                          builder: (context, likeState) {
                                        if (likeState is Likes) {
                                          return LikeButton(
                                            onTap: (isLiked) async {
                                              if (likeState.likes.contains(state
                                                  .catsImages[index].url)) {
                                                _likeBloc.add(Dislike(
                                                    id: state.catsImages[index]
                                                        .url));
                                                return true;
                                              } else {
                                                _likeBloc.add(Like(
                                                    id: state.catsImages[index]
                                                        .url));
                                                return false;
                                              }
                                            },
                                            size: 32,
                                            circleColor: CircleColor(
                                                start: Colors.red,
                                                end: Colors.red),
                                            bubblesColor: BubblesColor(
                                              dotPrimaryColor: Colors.red,
                                              dotSecondaryColor: Colors.red,
                                            ),
                                            likeBuilder: (_) {
                                              return Icon(
                                                CupertinoIcons.heart_fill,
                                                color: likeState.likes.contains(
                                                        state.catsImages[index]
                                                            .url)
                                                    ? Colors.red
                                                    : Colors.white70,
                                                size: 32,
                                              );
                                            },
                                          );
                                        } else {
                                          return Text(
                                              "Something went wrong...");
                                        }
                                      }),
                                    ],
                                  ),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                    );
                  }),
            );
          } else {
            return Center(
              child: SpinKitFadingCircle(
                size: 100,
                itemBuilder: (BuildContext context, int index) {
                  return DecoratedBox(
                    decoration: BoxDecoration(
                      color: index.isEven ? Colors.blue : Colors.blue[300],
                      borderRadius: BorderRadius.circular(180),
                    ),
                  );
                },
              ),
            );
          }
        });
  }
}
  • lib/features/cats/detail_page.dart
import 'package:cached_network_image/cached_network_image.dart';
import '/bloc/cat_facts/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class HeroScreen extends StatelessWidget {
  final String imgUrl;
  final String tag;

  const HeroScreen({Key? key, required this.imgUrl, required this.tag})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Material(
      type: MaterialType.transparency,
      child: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          flexibleSpace: Container(
            decoration: BoxDecoration(
                gradient: LinearGradient(
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                    colors: <Color>[Colors.blue, Colors.purple])),
          ),
        ),
        body: Center(
          child: Hero(
            tag: tag,
            flightShuttleBuilder: (
              BuildContext flightContext,
              Animation<double> animation,
              HeroFlightDirection flightDirection,
              BuildContext fromHeroContext,
              BuildContext toHeroContext,
            ) {
              return SingleChildScrollView(
                child: CachedNetworkImage(
                  imageUrl: '$imgUrl',
                ),
              );
            },
            child: Container(
              decoration: BoxDecoration(),
              child: Column(
                children: [
                  CachedNetworkImage(
                    imageUrl: '$imgUrl',
                  ),
                  // ListTile(leading: Icon(Icons.favorite, color: Colors.red)),
                  Expanded(
                    flex: 1,
                    child: SingleChildScrollView(
                      scrollDirection: Axis.vertical,
                      child: Flexible(
                        child: Padding(
                            padding: const EdgeInsets.only(top: 20),
                            child: Facts()),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class Facts extends StatelessWidget {
  const Facts({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: BlocBuilder<CatFactsBloc, CatFactsState>(
        builder: (context, state) {
          if (state is CatsFactsLoaded) {
            return Column(
              children: state.facts
                  .map(
                    (e) => Text('«$e»',
                        style: TextStyle(
                          color: Colors.blue.withOpacity(0.6),
                          fontSize: 20,
                          fontWeight: FontWeight.w500,
                        )),
                  )
                  .toList(),
            );
          } else {
            return Center(
              child: SpinKitFadingCircle(
                size: 50,
                itemBuilder: (BuildContext context, int index) {
                  return DecoratedBox(
                    decoration: BoxDecoration(
                      color: index.isEven ? Colors.blue : Colors.blue,
                      borderRadius: BorderRadius.circular(100),
                    ),
                  );
                },
              ),
            );
          }
        },
      ),
    );
  }
}
  • lib/features/favourite/favourite.dart
import '/bloc/cat_like/bloc.dart';
import '/cached/cached_network_images.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:like_button/like_button.dart';

class Favorites extends StatelessWidget {
  const Favorites({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    LikeBloc _bloc = BlocProvider.of<LikeBloc>(context);
    return BlocBuilder<LikeBloc, LikesState>(
      builder: (context, state) {
        if (state is Likes) {
          return GridView.builder(
            padding: EdgeInsets.all(3),
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3,
              mainAxisSpacing: 1,
              crossAxisSpacing: 1,
              childAspectRatio: 1,
            ),
            itemCount: state.likes.length,
            itemBuilder: (context, index) => Stack(
              children: [
                CachedImage(imageUrl: state.likes.elementAt(index)),
                Padding(
                  padding: EdgeInsets.all(8),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: [
                          BlocBuilder<LikeBloc, LikesState>(
                              builder: (context, likeState) {
                            if (likeState is Likes) {
                              return LikeButton(
                                  onTap: (isLiked) async {
                                    _bloc.add(Dislike(
                                        id: state.likes.elementAt(index)));
                                    return !isLiked;
                                  },
                                  circleColor: CircleColor(
                                      start: Colors.red, end: Colors.red),
                                  bubblesColor: BubblesColor(
                                    dotPrimaryColor: Colors.red,
                                    dotSecondaryColor: Colors.red,
                                  ),
                                  size: 32,
                                  likeBuilder: (bool isLiked) {
                                    return Icon(
                                      Icons.favorite,
                                      color:
                                          isLiked ? Colors.white70 : Colors.red,
                                      size: 30,
                                    );
                                  });
                            } else {
                              return Text("Something went wrong...");
                            }
                          }),
                        ],
                      )
                    ],
                  ),
                ),
              ],
            ),
          );
        } else {
          return Text('Loading likes..');
        }
      },
    );
  }
}
  • lib/features/home/home_1.dart
import '/bloc/bloc_authentification/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_signin_button/button_list.dart';
import 'package:flutter_signin_button/button_view.dart';

class Login extends StatelessWidget {
  const Login({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final _bloc = BlocProvider.of<AuthBloc>(context);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text('Cats App'),
          flexibleSpace: Container(
            decoration: BoxDecoration(
                gradient: LinearGradient(
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                    colors: <Color>[Colors.blue, Colors.purple])),
          ),
        ),
        backgroundColor: Colors.white,
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              SignInButton(
                Buttons.Facebook,
                onPressed: () {
                  _bloc.add(FacebookLoginEvent());
                },
              ),
              SignInButton(
                Buttons.Google,
                onPressed: () {
                  _bloc.add(GoogleLoginEvent());
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}
  • lib/features/home/home_2.dart
import '/features/cats/cats_screen.dart';
import '/features/favourite/favourite.dart';
import '/features/profile/profile.dart';
import 'package:flutter/material.dart';

class HomeScreenUser extends StatefulWidget {
  HomeScreenUser({Key? key}) : super(key: key);

  @override
  _HomeScreenUserState createState() => _HomeScreenUserState();
}

class _HomeScreenUserState extends State<HomeScreenUser> {
  int _currentIndex = 0;
  PageController _pageController = PageController();
  List<Widget> _screens = [
    CatsScreen(),
    Favorites(),
    UserProfile(),
  ];
  void _onPageChanged(int index) {}

  void onTapped(int _currentIndex) {
    _pageController.jumpToPage(_currentIndex);
  }

  @override
  Widget build(BuildContext context) => DefaultTabController(
        length: 3,
        child: Scaffold(
          backgroundColor: Colors.white,
          appBar: AppBar(
            centerTitle: true,
            title: Text('Cats App'),
            flexibleSpace: Container(
              decoration: BoxDecoration(
                  gradient: LinearGradient(
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                      colors: <Color>[Colors.blue, Colors.purple])),
            ),
          ),
          body: PageView(
            controller: _pageController,
            children: _screens,
            onPageChanged: _onPageChanged,
          ),
          bottomNavigationBar: BottomNavigationBar(
            selectedItemColor: Colors.blue,
            onTap: (index) {
              setState(() {
                _currentIndex = index;
                onTapped(_currentIndex);
              });
            },
            items: const <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.add_a_photo),
                label: "Cats",
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.favorite),
                label: "Favourite",
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.settings),
                label: "Profile",
              ),
            ],
            unselectedItemColor: Colors.blue,
          ),
        ),
      );
}
  • lib/features/profile/profile.dart
import 'package:cached_network_image/cached_network_image.dart';
import '/bloc/bloc_authentification/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_signin_button/button_builder.dart';

class UserProfile extends StatelessWidget {
  const UserProfile({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final _authBloc = BlocProvider.of<AuthBloc>(context);
    return Scaffold(
      body: Center(child: BlocBuilder<AuthBloc, AuthState>(
        builder: (context, state) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              SizedBox(
                height: 80,
              ),
              Container(
                width: 250,
                height: 250,
                decoration: BoxDecoration(
                    image: DecorationImage(
                      fit: BoxFit.cover,
                      image: CachedNetworkImageProvider(
                          '${state.currentUser?.photoURL}'),
                    ),
                    borderRadius: BorderRadius.circular(180),
                    color: Colors.white38),
              ),
              SizedBox(
                height: 30,
              ),
              Text(
                '${state.currentUser?.displayName}',
                style: TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.bold,
                    color: Colors.blue),
              ),
              SizedBox(
                height: 100,
              ),
              SignInButtonBuilder(
                text: 'Log Out',
                icon: Icons.logout,
                onPressed: () {
                  _authBloc.add(LogOutEvent());
                },
                backgroundColor: Colors.blue,
              )
            ],
          );
        },
      )),
    );
  }
}
  • lib/utils/auth.dart
import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final _auth = FirebaseAuth.instance;

  User? get user => _auth.currentUser;

  StreamSubscription<User?> get currentUser =>
      _auth.authStateChanges().listen((User? user) => user);

  Future<UserCredential> signInWithCredentials(AuthCredential credential) =>
      _auth.signInWithCredential(credential);

  Future<void> logout() => _auth.signOut();
}

Output

Screenshot (67)
Screenshot (70)
Screenshot (69)

GitHub Repository

Source Code and Assets: Flutter Cats App.

SHARE Cat App, A Flutter Mini Project

You may also like...

Leave a Reply

Your email address will not be published.

Share