1
How ToProgrammingSoftware

How to link flutter with firebase email password authentication

11 Mins read

I recently started learning flutter and I was looking for the simplest way to link flutter with firebase email password authentication. I found flutterfire(which is basically flutter and Firebase) and thought that it would be quite difficult to implement at first. But turns out, it was very easy to implement thanks to the very detailed documentation from the Flutterfire team. Let’s get started on how I was able to set this up.

In this blog, we will specifically look at how we can use email password authentication on firebase from our flutter app.

Prerequisites.

  • Basic understanding of dart and flutter
  • A google account that you can register on firebase
  • An IDE of your choice, I will be using Android studio for this.

By the end of this blog, you will be able to do the following.

  • Create a login page with username and password
  • Create a register page with username and password
  • Handle the authentication from Firebase(registering and login)
  • Test our app

Creating the UI design sketch for our application

For login page

  • Simple Text Logo
  • TextField(for entering username)
  • TextField(for entering password)
  • ElevatedButton(for login button)
  • Register Link

For Register page

  • Simple Text Logo
  • TextField(for entering username)
  • TextField(for entering password)
  • ElevatedButton(for register button)
  • Login Link

Always try to design before coding as this gives you clarity on what you are going to build. Sketching a design for what you have in mind helps to focus your creativity and lets you have the app in mind before writing any line of code.

Basically, we have a rough sketch of what we expect our design to look like as below. This gives us an idea of what we expect to have at the end of the coding session.

login flow ui sketch

You might have noticed in our app design sketch that we had a link to register and login on each page. This is because once you register for an account with firebase, you need to be able to authenticate yourself whenever you go back to your app. For our login page, we had a link to the login URL while for our register page, the link was registered with email authentication.

firebase email password authentication output flow


After we implement our authentication flow and build our UI, we will have screens similar to these, and of course they will be linked to firebase through the flutterfire package provided for us. We have intentionally left them as simple as possible, since we are mainly interested in linking firebase to our flutter app for this particular session!

Creating our email password authentication on flutter

Now that we have our sketch ready, let’s start by creating a new flutter project called AlphaApp.

Go ahead and open up your favorite IDE, Android studio if you are using android studio like me, I will be using that for this blog and let’s create a new flutter project by going onto File > New Project > Flutter Project > Next > Next < Finish(alphaApp) to create our flutter project.

Step 1

create new flutter project from android studio

Next, make sure you have flutter configured in your local environment or follow these instructions to have this done.
Next, name our app and click finish to have our project loaded onto our IDE.

You would see results similar to the following. Flutter gives you instructions on how to run the app. And let’s go ahead and run the default app to ensure that everything is set up in our system to run a flutter app.

NOTE: Make sure you have configured an emulator device before running our app!

And just like that, we should have the default flutter app running as shown.

Setting up firebase and flutterfirefor firebase email password authentication.

Navigate to the firebase console and create an account if you do not have one. After doing so, you should land on an empty projects dashboard if this is the first time you have signed up, if not, you will find that page with probably a few projects you had set up before. I have something like this.

Next, we need to create the project in firebase. Go ahead and click Create a project and name your project alpha-app(or a name of your choice). Follow through the next steps until you click finish and you should see your app ready on the dashboard.

If you select this newly created app, you should see something similar to this.



We now need to configure which authentication methods we will be using, and since we are using email password authentication, we can now navigate to the authentication settings to have this set up.

Under build > Authentication, click get started.

Enable the email Password authentication and click save

After this is successful, you should see the new provider listed!

And that’s it for the firebase side configurations! Let’s jump back to our IDE and begin adding our functionalities.


Creating the folder structure for our app

In order to organise our application in a better way, we will create logical folders and re-use some of the widgets that can be re-used such as the email and password inputs. Let’s create a folder structure like below.

Note that the alpha_logo.dart, email_input.dart and password_input.dart are under shared folder, while the dashboard.dart, login_page.dart and register_page.dart are under the authentication folder.
We have a service folder that has an email_password_auth.dart file that will make calls to firebase.

The main.dart file, has remained under the lib folder and is currently the only file with some code in it.

Adding required dependencies for our flutter app to integrate with firebase.

Adding required dependencies for our flutter app to integrate with firebase.
In order for flutter to communicate with firebase, we need to add firebase as a dependency. Open up your terminal on the app location and type in the following to add the dependencies.

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add flutterfire_cli

In order to download the dependencies and have them available on our project run:

flutter pub get

You could also use the IDE pub get link to achieve the same results.

NOTE: You could check the official docs which give more information on what is happening at these stages.

With these available, we now need to configure flutterfire. Run the following to initialize required flutter fire settings that will help us link our app to firebase.

But first, you need to login to firebase so that our account is linked on firebase!

On the terminal, type firebase login to authenticate your account. A pop up will allow you to login and if successfully logged in, you should find the following on your terminal.

Great! now let’s continue configuring our app.

Run the following commands on the terminal and select the app we just created on firebase.

$ dart pub global activate flutterfire_cli

$ flutterfire configure

You should have results similar to the following.

We have now successfully linked our app to firebase. A couple of config files will be created on your application. Take note of the firebase_options.dart file as we will reference it later on the app.

Creating a register page in flutter

For the register page, we need to have a text field for the email input, password input, elevated button and a login link. Open the register_page.dart and let’s type in the following.

// register_page.dart

import 'package:alpha_app/authentication/service/email_password_auth.dart';
import 'package:alpha_app/authentication/shared/alpha_logo.dart';
import 'package:alpha_app/authentication/shared/email_input.dart';
import 'package:alpha_app/authentication/shared/password_input.dart';
import 'package:flutter/material.dart';

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

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
  late final TextEditingController emailController;
  late final TextEditingController passwordController;

  @override
  void initState() {
    emailController = TextEditingController();
    passwordController = TextEditingController();
    super.initState();
  }

  @override
  void dispose() {
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Expanded(
                  child: Container(
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.background,
                      borderRadius: const BorderRadius.only(
                        topLeft: Radius.circular(50),
                        topRight: Radius.circular(50),
                      ),
                    ),
                    margin: const EdgeInsets.only(top: 60),
                    child: SizedBox(
                      width: 500,
                      child: SingleChildScrollView(
                        primary: true,
                        child: Column(
                          children: [
                            const SizedBox(height: 50),
                            const AlphaLogo(),
                            Center(
                              child: Container(
                                  alignment: Alignment.topLeft,
                                  margin: const EdgeInsets.only(
                                    left: 22,
                                    bottom: 20,
                                  ),
                                  child: const Center(
                                    child: Text("Register for an account."),
                                  )),
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Flexible(
                                  child: FractionallySizedBox(
                                    widthFactor: 0.8,
                                    child: Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.stretch,
                                        children: <Widget>[
                                          Center(
                                            child: Column(
                                              crossAxisAlignment:
                                                  CrossAxisAlignment.start,
                                              mainAxisAlignment:
                                                  MainAxisAlignment.spaceEvenly,
                                              children: [
                                                EmailInput(
                                                    emailController:
                                                        emailController),
                                                PasswordInput(
                                                    passwordController:
                                                        passwordController),
                                                const SizedBox(height: 20),
                                                Row(
                                                  mainAxisAlignment:
                                                      MainAxisAlignment.center,
                                                  children: [
                                                    Flexible(
                                                      child:
                                                          FractionallySizedBox(
                                                        widthFactor: 0.55,
                                                        child: SizedBox(
                                                          height: 36.0,
                                                          child: ElevatedButton(
                                                            onPressed:
                                                                () async {
                                                              await EmailPasswordAuth()
                                                                  .registerUserWithEmailAndPassword(
                                                                      emailController
                                                                          .text,
                                                                      passwordController
                                                                          .text,
                                                                      context);
                                                            },
                                                            child: Text(
                                                              "Register",
                                                              style: TextStyle(
                                                                fontWeight:
                                                                    FontWeight
                                                                        .w400,
                                                                fontSize: Theme.of(
                                                                        context)
                                                                    .textTheme
                                                                    .titleMedium
                                                                    ?.fontSize,
                                                              ),
                                                            ),
                                                          ),
                                                        ),
                                                      ),
                                                    ),
                                                  ],
                                                ),
                                                Container(
                                                  // width: double.infinity,
                                                  height: 20,
                                                  alignment: Alignment.center,
                                                  margin: const EdgeInsets.only(
                                                      top: 10),
                                                ),
                                                const SizedBox(height: 20),
                                                Row(
                                                  mainAxisAlignment:
                                                      MainAxisAlignment.center,
                                                  crossAxisAlignment:
                                                      CrossAxisAlignment.center,
                                                  children: [
                                                    TextButton(
                                                      onPressed: () {
                                                        Navigator.pushNamed(
                                                            context, "/");
                                                      },
                                                      child: const Text(
                                                        "Back to Login",
                                                        style: TextStyle(
                                                            decoration:
                                                                TextDecoration
                                                                    .underline,
                                                            fontSize: 15),
                                                      ),
                                                    ),
                                                  ],
                                                ),
                                              ],
                                            ),
                                          ),
                                        ]),
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Our login page.dart contains the following

// login_page.dart

import 'package:alpha_app/authentication/service/email_password_auth.dart';
import 'package:alpha_app/authentication/shared/alpha_logo.dart';
import 'package:alpha_app/authentication/shared/email_input.dart';
import 'package:alpha_app/authentication/shared/password_input.dart';
import 'package:flutter/material.dart';

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

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  late final TextEditingController emailController;
  late final TextEditingController passwordController;

  @override
  void initState() {
    emailController = TextEditingController();
    passwordController = TextEditingController();
    super.initState();
  }

  @override
  void dispose() {
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Expanded(
                  child: Container(
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.background,
                      borderRadius: const BorderRadius.only(
                        topLeft: Radius.circular(50),
                        topRight: Radius.circular(50),
                      ),
                    ),
                    margin: const EdgeInsets.only(top: 60),
                    child: SizedBox(
                      width: 500,
                      child: SingleChildScrollView(
                        primary: true,
                        child: Column(
                          children: [
                            const SizedBox(height: 50),
                            const AlphaLogo(),
                            Center(
                              child: Container(
                                  alignment: Alignment.topLeft,
                                  margin: const EdgeInsets.only(
                                    left: 22,
                                    bottom: 20,
                                  ),
                                  child: const Center(
                                    child: Text("Login for an account."),
                                  )),
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Flexible(
                                  child: FractionallySizedBox(
                                    widthFactor: 0.8,
                                    child: Column(
                                        crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                        children: <Widget>[
                                          Center(
                                            child: Column(
                                              crossAxisAlignment:
                                              CrossAxisAlignment.start,
                                              mainAxisAlignment:
                                              MainAxisAlignment.spaceEvenly,
                                              children: [
                                                EmailInput(
                                                    emailController:
                                                    emailController),
                                                PasswordInput(
                                                    passwordController:
                                                    passwordController),
                                                const SizedBox(height: 20),
                                                Row(
                                                  mainAxisAlignment:
                                                  MainAxisAlignment.center,
                                                  children: [
                                                    Flexible(
                                                      child:
                                                      FractionallySizedBox(
                                                        widthFactor: 0.55,
                                                        child: SizedBox(
                                                          height: 36.0,
                                                          child: ElevatedButton(
                                                            onPressed:
                                                                () async {
                                                              await EmailPasswordAuth().loginWithEmailAndPassword(context,
                                                                  emailController
                                                                      .text,
                                                                  passwordController
                                                                      .text);
                                                            },
                                                            child: Text(
                                                              "Login",
                                                              style: TextStyle(
                                                                fontWeight:
                                                                FontWeight
                                                                    .w400,
                                                                fontSize: Theme.of(
                                                                    context)
                                                                    .textTheme
                                                                    .titleMedium
                                                                    ?.fontSize,
                                                              ),
                                                            ),
                                                          ),
                                                        ),
                                                      ),
                                                    ),
                                                  ],
                                                ),
                                                Container(
                                                  // width: double.infinity,
                                                  height: 20,
                                                  alignment: Alignment.center,
                                                  margin: const EdgeInsets.only(
                                                      top: 10),
                                                ),
                                                const SizedBox(height: 20),
                                                Row(
                                                  mainAxisAlignment:
                                                  MainAxisAlignment.center,
                                                  crossAxisAlignment:
                                                  CrossAxisAlignment.center,
                                                  children: [
                                                    TextButton(
                                                      onPressed: () {
                                                        Navigator.pushNamed(
                                                            context, "/register");
                                                      },
                                                      child: const Text(
                                                        "Register",
                                                        style: TextStyle(
                                                            decoration:
                                                            TextDecoration
                                                                .underline,
                                                            fontSize: 15),
                                                      ),
                                                    ),
                                                  ],
                                                ),
                                              ],
                                            ),
                                          ),
                                        ]),
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ],
      ),
    );
  }
}

dashboard.dart

//dashboard.dart 

import 'package:flutter/material.dart';

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

  @override
  State<Dashboard> createState() => _DashboardState();
}

class _DashboardState extends State<Dashboard> {
  DateTime time = DateTime.now();

  @override
  Widget build(BuildContext context) {
    Stream timeListener = getCurrentTime();
    timeListener.listen((receiveDate) {
      setState(() {
        time = receiveDate;
      });
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('Dashboard'),
      ),
      body: Column(
        children: [
          const SizedBox(
            height: 30,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              Text(
                "Welcome to Alpha App",
                style: TextStyle(
                  fontSize: 30,
                ),
              ),
            ],
          ),
          const SizedBox(
            height: 60,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              Text(
                "The time is:",
                style: TextStyle(
                  fontSize: 40,
                ),
              ),
            ],
          ),
          const SizedBox(
            height: 80,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                "${time.hour}: ${time.minute}: ${time.second}",
                style: const TextStyle(
                  fontSize: 60,
                ),
              ),
            ],
          ),
          const SizedBox(
            height: 80,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextButton(
                onPressed: () {
                  Navigator.pushNamed(
                      context, "/");
                },
                child: const Text(
                  "Back to Login",
                  style: TextStyle(
                      decoration:
                      TextDecoration
                          .underline,
                      fontSize: 15),
                ),
              )
            ],
          )
        ],
      ),
    );
  }

  Stream<DateTime> getCurrentTime() async* {
    DateTime now = DateTime.now();
    DateTime date = DateTime(now.year, now.month, now.day);
    await Future.delayed(const Duration(seconds: 1));
    print("Time: $now");
    yield now;
  }
}

Our password_input.dart

// shared/password_input.dart

import 'package:flutter/material.dart';

class PasswordInput extends StatelessWidget {
  const PasswordInput({
    Key? key,
    required this.passwordController,
  }) : super(key: key);

  final TextEditingController passwordController;

  @override
  Widget build(BuildContext context) {
    return Container(
        width: double.infinity,
        height: 70,
        margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
        padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
        decoration: BoxDecoration(
          border: Border.all(width: 1),
          borderRadius: const BorderRadius.all(
            Radius.circular(20),
          ),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            const Icon(Icons.password_outlined),
            Expanded(
              child: Container(
                margin: const EdgeInsets.only(left: 10),
                child: TextFormField(
                  controller: passwordController,
                  maxLines: 1,
                  obscureText: true,
                  decoration: const InputDecoration(
                    label: Text(" Password ..."),
                    border: InputBorder.none,
                  ),
                ),
              ),
            ),
          ],
        ));
  }
}

email_input.dart

//shared/email_input.dart

import 'package:flutter/material.dart';

class EmailInput extends StatefulWidget {
  const EmailInput({
    Key? key,
    required this.emailController,
  }) : super(key: key);

  final TextEditingController emailController;

  @override
  State<EmailInput> createState() => _EmailInputState();
}

class _EmailInputState extends State<EmailInput> {
  String? errorMessage;

  @override
  Widget build(BuildContext context) {
    return Container(
        width: double.infinity,
        height: 70,
        margin: const EdgeInsets.symmetric(
          horizontal: 20,
          vertical: 20,
        ),
        padding: const EdgeInsets.symmetric(
          horizontal: 15,
          vertical: 5,
        ),
        decoration: BoxDecoration(
          border: Border.all(width: 1),
          borderRadius: const BorderRadius.all(Radius.circular(20)),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            const Icon(Icons.email_outlined),
            Expanded(
              child: Container(
                margin: const EdgeInsets.only(left: 10),
                child: TextFormField(
                  controller: widget.emailController,
                  autocorrect: false,
                  maxLines: 1,
                  decoration: const InputDecoration(
                      label: Text(" E-mail ..."),
                      border: InputBorder.none,
                      errorStyle: TextStyle(

                          fontSize: 0,
                          height: 0,
                          decoration: TextDecoration.lineThrough

                      )
                  ),
                  validator: (text) {
                    final txt = text;
                    validateEmail(txt!);
                  },
                  autovalidateMode: AutovalidateMode.onUserInteraction,
                ),
              ),
            )
          ],
        ));
  }

  String? validateEmail(String text) {
    if (text == null || text.isEmpty) {
      return 'Can\'t be empty';
    }
    if (text.length < 4) {
      return 'Too short';
    }
    return null;
  }
}

alpha_logo.dart

//shared/alpha_logo.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        alignment: Alignment.topLeft,
        margin: const EdgeInsets.only(
          left: 22,
          bottom: 20,
        ),
        child: Center(
            child: Text(
              "Alpha App",
              style: TextStyle(
                fontWeight: FontWeight.w900,
                fontSize: Theme.of(context)
                    .textTheme
                    .headline3
                    ?.fontSize,
              ),
            )),
      ),
    );
  }
}

email_password_auth.dart

// service/email_password_auth.dart

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

class EmailPasswordAuth {
  Future<void> registerUserWithEmailAndPassword(String email_, String password_,
      BuildContext context) async {
    try {
      await FirebaseAuth.instance
          .createUserWithEmailAndPassword(email: email_, password: password_);
      showSnackBar(context, 'Successfuly registered. Please login.');
      Navigator.popAndPushNamed(context, '/');
    } on FirebaseAuthException catch (e) {
      if (e.code == 'weak-password') {
        showSnackBar(context, 'Weak password, try set a stronger password');
      }
      if (e.code == 'email-already-in-use') {
        showSnackBar(context, 'Email already registered');
      }
      else {
        // Note: It's note recommended to use print statements in a prod environment.
        print("Error: $e");
        showSnackBar(context, 'Something happened and we could not log you in');
      }
    }
  }

  Future<void> loginWithEmailAndPassword(BuildContext context,
      String email,
      String password) async {
    try {
      // Sign in the user on firebase with provided username and password
      await FirebaseAuth.instance
          .signInWithEmailAndPassword(email: email, password: password);

      final currentUserObject = FirebaseAuth.instance.currentUser;

      if (currentUserObject?.uid != null) {
        showSnackBar(context, 'Logging you in...');
        Navigator.popAndPushNamed(context, '/dashboard');

      }
    } on FirebaseAuthException catch (e) {
      if (e.code == 'user-not-found') {
        showSnackBar(context, 'The user with that email does not exist!');
      }
      if (e.code == 'invalid-email') {
        showSnackBar(context, 'Invalid email address, Please check again');
      } else {
        showSnackBar(context, 'Something happened and we could not log you in');
      }
    }
  }

}
// This is a snackbar that will display error messages to the user on the app.
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(
    BuildContext context, message) {
  return ScaffoldMessenger.of(
      context)
      .showSnackBar(
      SnackBar(
        content: Text(message),
      ));
}

And finally, we have edited our main.dart file to contain the following.

import 'package:alpha_app/authentication/dashboard.dart';
import 'package:alpha_app/authentication/login_page.dart';
import 'package:alpha_app/authentication/register_page.dart';
import 'package:alpha_app/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Alpha App',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      home: const LoginPage(),
      routes: {
        '/register':(context)=>const RegisterPage(),
        '/dashboard':(context)=>const Dashboard(),
      },
    );
  }
}

The main file basically starts by loading the login page and will only redirect to the dashboard after correct authentication has been done.

If you would like to download the code – head over to this Github repository and download the project files.

Testing our flutter firebase email password authentication

In order to ensure our app is working as expected, click the register link and input a valid email and password, and click register.

Head over to the firebase Users tab and ensure that you user has been registered on the dashboard. And if all is well, you should have the following.

If you have a user on the dashboard, then congratulations! You have successfully linked your flutter app to firebase email password provider authentication.

Now head over to the login page and ensure you can login to the dashboard using the newly created credentials! – Which on sign in, should redirect you straight to the dashboard!

Conclusion

And just like that! We now have an authenticated app by leveraging on flutterfire. Hope you enjoyed this!

Keep an eye on this blog to learn more about flutter.

You might be interested in how to write clean code.

Don’t miss amazing tips!

1
Related posts
How To

How To Implement Data Tables In Cucumber Using Selenium Ruby

17 Mins read
Table of contents0.1 Advantages of Behavior Driven Development0.2 What is Cucumber?0.3 What are data tables in Cucumber?0.4 How to implement data tables…
How ToProgrammingSoftware

How To Write Clean Code

3 Mins read
Let’s look at tips and tricks on how to write clean code. Everyone wants to be able to write code that is…
How To

Is The Universe Infinite or Finite

3 Mins read
Table of contents0.1 What is cosmic inflation?0.2 How is space measured?0.3 Conclusion1 Don’t miss amazing tips! Is the Universe infinite or finite….

Leave a Reply

Your email address will not be published. Required fields are marked *

+ 3 = 13

×
Crypto

Central African Republic adopts bitcoin as an official currency