Image for post
Image for post

Originally published on bendyworks.com.

This is it. 31 blog posts in 31 days. Writing a month of flutter has been a ton of work but also lots of fun and a good learning experience. I really appreciate how supportive and and positive everyone as been.

Publishing experience

For the series I’ve been posting on bendyworks.com, DEV, my personal blog, and Medium. After publishing to these sites, I would put the Bendyworks link on Twitter, Reddit, and the Flutter Study Group Slack.

Posting to DEV was easy as they use Markdown just like the Bendworks blog. DEV also has built in support for a series of posts so it’s easy to read the entire series. I did have to manually upload any embedded images. DEV also has a number of liquid tags for embedding things like GitHub issues that I didn’t make as much use of as I should have. …


Image for post
Image for post

Originally published on bendyworks.com.

For the last post before the month’s wrap up tomorrow, I wanted to do something more fun: use a hero animation between the home page list and the individual post page.

When I first implemented the Hero animation it never worked going back from a PostPage to the HomePage. The reason was that HomePage would get rerendered and that would generate new fake posts. So I moved the fake data generation up a level to MyApp and pass it into HomePage. This is more realistic as going to the HomePage shouldn't request the Posts every time.

HomePage(
title: 'Birb',
posts: _loadPosts(context)…


Image for post
Image for post

Originally published on bendyworks.com.

Yesterday I implemented saving new users to Firestore but I wasn’t happy with the implementation. So today I refactored everything, well not everything but a lot. There are still areas for improvement but I like the general pattern being used now.

Now there is a top level ScopedModel that tracks the current authentication status. This sets up a listener on FirebaseAuth and Firestore and will get pushed changes when either have changed. Children widgets that need to change functionality based on authentication status will get rerendered as needed.

A couple of the less important changes that I’ll get out of the way now. …


Image for post
Image for post

Originally published on bendyworks.com.

Today was supposed to be simple. Take form values, save them in Firestore. It works but the current implementation is messy so I’m going to walk through the work in progress (WIP) code and refactor it tomorrow.

The larger architectural change was creating a UserService to handle getting and creating users. This approach works but creates a complex dependency injection pattern that requires a lot of duplicate code and mocking in test. These are some of the current changes and what I don't like about the implementation:

RegisterPage now takes a UserService which in turn takes FirebaseAuth and Firestore instances. Doing this once wouldn't be so bad but I've had to instantiate UserService in several places. I'd like to find a better approach than UserService. …


Image for post
Image for post

Originally published on bendyworks.com.

When a user signs in with Google I’m going to create a user document in Firestore. Each authenticated user should only be able to create one user document. These documents will eventually be readable by other users so Firestore needs to have server(less)-side validation to keep the data as correct as possible.

Right now I’m working on registering users so I’m only going to implement create rules, not read, update, etc.

match /users/{userId} {
allow create: if isOwner(userId) &&
// TODO: enable after bug fix https://github.com/firebase/firebase-tools/issues/1073
// validCreateTimestamps() &&
validCreateUser();
}

This will allow user documents to be created if the owner has the same ID and the document being created passes validation. …


Image for post
Image for post

Originally published on bendyworks.com.

One aspect of using Firestore for my data backend means I need to be certain my security rules are configured correctly. Otherwise users might be able to read or write date they shouldn’t have access to.

A few days ago I set up Firestore in the server directory. I'm going to continue that work and configure tests to run on the Firestore emulator based off of the typescript-qickstart example.

In package.json I'll add some devDependencies and define several scripts. Node package scripts can be run with npm run <name>.

  • postinstall will set up the Firestore emulator after npm install is run in the scripts


Image for post
Image for post

Originally published on bendyworks.com.

For this festive day of Christmas, I’m going to do something more fun and add a fancy icon. For android I will be using an adaptive icon so that it looks good in any shape and has a nice wiggle of movement.

A big thank you goes to @tommy_emo_ for creating this awesome design.

Image for post
Image for post

There is a nice flutter_launcher_icons package that makes the technical implementation easy.

To start, require flutter_launcher_icons as a dev_dependency in pubspec.yaml. Then there will be a new flutter_icons key added with several values.

flutter_icons:
android: true
ios: true
image_path_android: assets/icon/ic_launcher_xxxhdpi.png
image_path_ios: assets/icon/ios.png
adaptive_icon_background: assets/icon/ic_background.png …


Image for post
Image for post

Originally published on bendyworks.com.

After a user signs in with Google and registers, their info needs to be saved to a databasee. I’m going to use Firebase Firestore as my backend. Within the birb codebase I'm going to create a server directory and initialize a Firestore project inside it using firebase-tools.

$ firebase init######## #### ########  ######## ########     ###     ######  ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ######…


Image for post
Image for post

Originally published on bendyworks.com.

With the new user registration form in place, it’s time to make sure the form is tested and will work as expected.

There are basically five different states that need to be tested.

Default state

This is the view users will first arrive to and here I’m testing that all the components are present as expected.

testWidgets('Renders', (WidgetTester tester) async {
await tester.pumpWidget(app);

expect(find.text('Register'), findsOneWidget);
expect(find.text('I agree to the Terms of Services and Privacy Policy'),
findsOneWidget);
expect(find.byType(TextFormField), findsNWidgets(2));
expect(find.byType(OutlineButton), findsOneWidget);
expect(find.byType(Checkbox), findsOneWidget);
});

To succesfully submit the form, I require the user to provide a nickname and a full name. In the test those values will be provided with enterText. After filling out the two TextFormFields and submitting the form, I wait a tick for the success SnackBar to render. …


Image for post
Image for post

Originally published on bendyworks.com.

After a user navigates to the registration page, they should be able to enter their name and agree to the Terms of Service/Privacy Policy.

I’ll start by updating RegisterPage to render a RegisterForm widget that I'll create in a minute. Wrapped around the form is a SingleChildScrollView. This scroll view is for when the keyboard in open and the form can't fit in the visible space.

Scaffold(
appBar: AppBar(
title: const Text('Register'),
centerTitle: true,
elevation: 0.0,
),
body: const SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
child: RegisterForm(),
),
),
);

The majority of the work is going to be handled in the new RegisterForm. The initial StatefulWidget structure is based on Flutter's building a form with validsation recipe. …

About

Abraham Williams

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store