insert cheesy YT banner here :)

Flutter App’s can be easy…

Flutter is very powerful. It is quite easy to add complexity to a project, but if you stay within the confines of what is “out of the box” you can put together polished apps in minutes! In this example we will create an Owen Wilson “WOW” Soundboard app that includes:

We are going to build this app, in 10 minutes, with the following features:

Audio/Sound Output

Fun Animations

Grid Builder

User taps, Owen WOWWWs 🔊🔊🔊

Getting Started:

Install Flutter Install Flutter for Visual Studio Code Create a new project by typing Flutter: Create new project in the Command Palette

Let’s build the app, it will be divided into three sections. 1.) The Layout 2.) Add Assets — Pics + Sounds 3.) Animations

1.) The Layout

Get rid of the default main.dart contents

Once your create a new project, you will be given a default “increment counter” app. The entire project (that you care of at this point) lives in main.dart . Let’s replace that section with (below). Take note, below is mostly just getting rid of the default app and comments.

So now we have:

Add GridView.builder

The GridView.builder creates a scrollable, 2D array of widgets that are created on demand. Perfect for what we need! An array of Owen Wilson faces that we will tap to get a wownderful sound. The GridView.builder allows you to specify how many columns you want, spacing between them, etc.

GridView.builder(

shrinkWrap: true,

itemCount: 15,

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

crossAxisSpacing: 15,

mainAxisSpacing: 15,

crossAxisCount: 3,

),

itemBuilder: (BuildContext context, int index) => GestureDetector(

child: Text('${index + 1}'),

onTap: () {

if (mounted) {

setState(() {

print(index);

});

}

},

),

primary: false,

padding: const EdgeInsets.all(30),

),

A few things to note about GridView.builder :

crossAxisSpacing is the horizontal padding between widgets, mainAxisSpacing as vertical padding

is the horizontal padding between widgets, as vertical padding crossAxisCount is the number of columns

is the number of columns itemCount is the number of items in your grid. For “infinite” lists you’ll need to manually update the count.

is the number of items in your grid. For “infinite” lists you’ll need to manually update the count. The contents of itemBuilder are the widget items in your grid. Mine are wrapped with a GestureDetector to detect user actions like a click. Anything can go in here!

Above gives us a barebones grid. Let’s make each item in the grid a circle by replacing the contents of our itemBuilder to be a simple circle with the index in the center using the Container widget below.

child: Container(

decoration: BoxDecoration(

color: Colors.blue,

borderRadius: new BorderRadius.circular(100.0),

),

child: Center(

child: Text('${index + 1}'),

),

),

Putting the above together gives you the following. And at this point, it can all be run/created in dartpad.dev by clicking here.

2.) Add Assets — Pics and WOWs

Adding Pics / Owen Faces 🖼

Dartpad.dev is great for playing with STOCK Flutter components, but it will not let you import any assets or third party libraries. So from here on out, dartpad.dev will not do. But hey, you’re a dev so let’s do the real thing!

If you come from a web background, you’re probably used to project assets being immediately available. In Flutter-world you need to declare assets you will be using in your pubspec.yaml with a reference to their physical location in your project.

Create an assets directory with sounds and images as sub-directories. Add all of the assets from my Github Repo.

Kewl, so now we can use all these images and sounds. In our main.dart file under the _MyHomePageState stateful widget we can create a list of our images:

List<String> _headshots = [

'assets/images/owen1.png',

'assets/images/owen2.png',

'assets/images/owen3.png',

'assets/images/owen4.png',

'assets/images/owen5.png',

'assets/images/owen6.png',

];

Then, in the container used for each item in the Grid, we can add a DecorationImage which is essentially a “background” of sorts for a container.

decoration: new BoxDecoration(

image: new DecorationImage(

image: new AssetImage(_headshots[index % 5]),

fit: BoxFit.fill,

)

Using _headshots[index % 6] simply recycles the 6 Owen images we have (since there are 15 sounds / grid items).

COOL, so now we have Owen’s face on all of the grid items.

Adding Sound / Owen WOWs🔉

For audio playback in Flutter, unfortunately there is no native support so we need to use a third-party library. Albeit, the audioplayers library is feature packed, works on iOS and Android and is ez-pz to use.

To add audioplayers to our project we need to add audioplayers: any to our pubspec.yml and then import the packages in main.dart like below:

import 'package:audioplayers/audio_cache.dart';

import 'package:audioplayers/audioplayers.dart';

For the mp3 files we are going to create a class to easily reference them. To do this, let’s create a new sounds.dart file:

class WowSounds {

List<String> _wows = [

'sounds/wow1.mp3',

'sounds/wow2.mp3',

'sounds/wow3.mp3',

'sounds/wow4.mp3',

'sounds/wow5.mp3',

'sounds/wow6.mp3',

'sounds/wow7.mp3',

'sounds/wow8.mp3',

'sounds/wow9.mp3',

'sounds/wow10.mp3',

'sounds/wow11.mp3',

'sounds/wow12.mp3',

'sounds/wow13.mp3',

'sounds/wow14.mp3',

'sounds/wow15.mp3',

];

get wows => _wows;

}

In our main.dart file we will import our new dart file using:

import 'package:owowen_wilson/sounds.dart';

And instantiate the AudioCache (which stores audio in memory) and AudioPlayer in our _MyHomePageState Class. We will then reference then create a list, _wows , to store the reference to the mp3 files:

/// Audio Cache

AudioCache audioCache;

AudioPlayer audioPlayer; final List _wows = WowSounds().wows;

note: _wows is functionally the same as _headshots . The difference being that _wows is a bit cleaner since we keep the list in a separate file/Class.

Alrighty, so at this point… the Audio Player is set up, and the mp3’s refs are available. We need to load them into the Audio player. We will do this initial load in the Stateful Widget’s initState() method that runs the first time the widget is rendered. Including dispose() ensures the widget is disposed of properly when not in use:

@override

void initState() {

super.initState();

initSounds();

} void initState() {super.initState();initSounds(); @override

void dispose() {

super.dispose();

} void dispose() {super.dispose();

Above, you can see we call initSounds() . We need to add two functions to our project to initialize sounds, and play sounds. These should be added as functions in your _MyHomePageState class:

void initSounds() async {

audioPlayer = AudioPlayer();

audioCache = AudioCache(fixedPlayer: audioPlayer);

audioCache.loadAll(_wows);

} void playSound(wow) async {

var fileName = wow;

if (audioPlayer.state == AudioPlayerState.PLAYING) {

audioPlayer.stop();

}

audioPlayer = await audioCache.play(fileName);

}

initSounds() loads all of Owen’s WOW mp3’s into the cache so they can be played immediately

loads all of Owen’s WOW mp3’s into the cache so they can be played immediately playSound(wow) will play the file when this function is called

Only thing left to do is trigger playSound(wow) whenever a Grid item (one of Owen’s faces) is clicked. Reminder, this is where we initially had print(index) :

onTap: () {

if (mounted) {

setState(() {

playSound(_wows[index]);

});

}

}

Try it out! Owen should now “WOW!” each time you click his one of his faces.

3.) Animations

Flutter is an animation machine. Let’s make the currently selected Owen bounce /grow in size when he’s tapped.

Out of the box, Flutter can handle animations behind the scenes using pre-built widgets such as AnimatedContainer . These powerful little widgets will animate between size and color whenever a value is changed (the value must change in setState() for the widget to know there’s been a change).

To animate a face on tap, all we need to do is the following:

Add indexIsPlaying to the setState() function when a face is tapped:

setState(() {

playSound(_wows[index]);

indexIsPlaying = index;

});

This value will change to the current index that is being played whenever a user taps a new face. We will use indexIsPlaying as a boolean to signify whether or not one of the faces should animate by adding the following AnimatedContainer :

AnimatedContainer(

width: indexIsPlaying == index ? 120 : 80,

height: indexIsPlaying == index ? 120 : 80,

duration: Duration(milliseconds: 400),

curve: Curves.bounceOut,

decoration: BoxDecoration(

color: Colors.yellow[500],

borderRadius: new BorderRadius.circular(100.0),

image: new DecorationImage(

image: new AssetImage(_headshots[index % 5]),

fit: BoxFit.fill,

),

),

When indexIsPlaying == index then height and width of the face will be 120, if it’s not playing, it will be 80

then height and width of the face will be 120, if it’s not playing, it will be 80 AnimatedContainer handles the animation for you! You just need to “toggle” between the two sizes, and AnimatedContainer will handle everything in-between

handles the animation for you! You just need to “toggle” between the two sizes, and will handle everything in-between You can also animate position and color. And specify curve for how the animation looks

Putting it all together and you get a WOWNDERFUL Flutter app!

Wow! 🔊🔊🔊🔊

Wrapping up

Full project is available here: https://github.com/cookmscott/OwenWilsonSoundboard

Putting these pieces together, you can build even more beautiful and fun apps like this 4k Gradient Wallpaper App I created. Let me know if you’re interested in a Hero’s animation tutorial!