Home > front end >  Flutter common background image for all routes
Flutter common background image for all routes

Time:11-21

I am trying to add a background image which is always there no matter which route is active. I the example below is inspired by this answer but the background will only be visible for the route "/". I was hoping to not have to set the background image for each route. Any suggestions?

Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    home: const BoxDecoration(
      image: DecorationImage(
          image: AssetImage("assets/images/camping-background.png"),
          fit: BoxFit.cover),
    ),
    routes: <String, WidgetBuilder>{
      '/login': (BuildContext context) => const Login(),
      '/register': (BuildContext context) => const Register(),
      '/home': (BuildContext context) => const Home(),
    },
  );
}

CodePudding user response:

You may use the builder field on MaterialApp to provide a TransitionBuilder function that defines a common wrapper for all routes:

import 'package:flutter/material.dart';

void main() {
  runApp(Example());
}

class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      builder: (context, child) => DecoratedBox(
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: NetworkImage("https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg"),
            fit: BoxFit.cover,
          ),
        ),
        child: child,
      ),
      initialRoute: '/login',
      routes: <String, WidgetBuilder>{
        '/login': (BuildContext context) => Login(),
        '/home': (BuildContext context) => Home(),
      },
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Home'),
        ElevatedButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Go back')),
      ]
    );
  }
}

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Login'),
        ElevatedButton(onPressed: () => Navigator.of(context).pushNamed('/home'), child: const Text('Login')),
      ]
    );
  }
}

It takes a BuildContext as well as the currently rendered route as child as arguments and returns a Widget that is then displayed as the route.


Also since it seemed like there was some confusion with regards to the usage of home and routes, here is a snipped from the MaterialApp docs:

  1. For the / route, the home property, if non-null, is used.
  2. Otherwise, the routes table is used, if it has an entry for the route.
  3. Otherwise, onGenerateRoute is called, if provided. It should return a non-null value for any valid route not handled by home and routes.
  4. Finally if all else fails onUnknownRoute is called.

While you could use home and routes together, I personally thing it's more clear what's going on with routes using only routes in addition to initialRoute to indicate which is first (unless it is indeed / which is the default).

CodePudding user response:

Copy & Paste it on dartpad to see the results

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class BaseLayout extends StatelessWidget {
  final Widget? child;

  const BaseLayout({Key? key, @required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 720,
      decoration: const BoxDecoration(
        image: DecorationImage(
            image: NetworkImage(
                "https://images.pexels.com/photos/440731/pexels-photo-440731.jpeg"),
            fit: BoxFit.fill),
      ),
      child: child,
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Home')),
        body: BaseLayout(child: Home()),
      ),
      routes: <String, WidgetBuilder>{
        '/login': (BuildContext context) => Login(),
        '/register': (BuildContext context) => Register(),
        '/home': (BuildContext context) => Home(),
      },
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('HOMEPAGE', style: TextStyle(fontSize: 32)),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('Login'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => Scaffold(
                    appBar: AppBar(title: const Text('Login')),
                    body: BaseLayout(child: Login())),
              ),
            );
          },
        ),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('Register'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => BaseLayout(child: Register())),
            );
          },
        ),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('No Background Image Screen'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => NoBackground()),
            );
          },
        ),
      ],
    );
  }
}

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'Login',
          style: Theme.of(context).textTheme.headline4,
        ),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('Back to Homepage'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ],
    );
  }
}

class Register extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'Register!',
          style: Theme.of(context).textTheme.headline4,
        ),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('Back to Homepage'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ],
    );
  }
}

class NoBackground extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text(
          'No Background Image!',
          style: TextStyle(color: Colors.white),
        ),
        const SizedBox(height: 12),
        ElevatedButton(
          child: const Text('Back to Homepage'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ],
    );
  }
}

Complete with scaffold app bar & no background image sample

Check it on Gist: Complete code here

  • Related