skip to Main Content

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(),
    },
  );
}

2

Answers


  1. Updated answer

    For the sake of a background image, you can simply wrap your MaterialApp in the DecoratedBox. This is preferable to the other approach (outlined further below) because that abuses builder which is intended for other purposes:

    A builder for inserting widgets above the Navigator or – when the WidgetsApp.router constructor is used – above the Router but below the other widgets created by the WidgetsApp widget, or for replacing the Navigator/Router entirely.

    As MaterialApp solely configures non-rendered widgets and the DecoratedBox doesn’t rely on any of them, it can simply serve as parent widget to the rest of the app achieving the desired effect.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(Example());
    }
    
    class Example extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return 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: MaterialApp(
            title: 'Flutter Demo',
            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')),
          ]
        );
      }
    }
    

    Previous answer

    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).

    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search