Manage Your Environment Flutter Projects without New Build with Pigeon and Swift

Veli Bacık
Flutter Community
Published in
9 min readMay 19, 2022

We have a lot of solutions by config management for instance locally, backend, etc. This time let’s learn how to implement native solutions, then we can handle the environment, user and etc.

Let’s think about what is our plan and why we need native solutions. Normally life we need configuration management for the dev, and prod environment as basically. And our community uses many scenarios for this requirement, for instance:

  • Flavor that’s pretty enough for management using native knowledge.
  • Dotenv that’s another better solution. Actually, I choose dotenv and I gonna implement every environment configuration.
  • Remote config finally that’s our best solution for the mobile world because we can update our configuration every time other solutions generally statically work.

This article is similar to these solutions but we gonna use native power only for development time. Perhaps, you thinking about why we need native solutions and why only iOS?. So let's move on to this solution.

My main target is to use native dependencies so these topics call it like these:

This project settings
Reference

I just tell you about the iOS side. Probably I will make android side detail but that’s not a big problem because this article will be taught:

  • Pigeon usage for the native side
  • Native library binding with flutter project.

So let’s move on coding side time is ready.

Pigeon Implementation

You should read this article before implementing this library. It will teach the native bridge concepts. Thus after we gonna do these steps while starting the code:

1- Define your schema according to your actions.

This is mean: Let’s think about we can find the book to realm db from the ios native side. You should need a book model, and find the book method. We’re going to create book.dart(top of the project some the library path) after implementing this model with the interface.

This will generate native code for both objective-c and java. (If you want to use swift, you have to create a native bridge. I’ll explain how will you do it)

2- Add to pigeon library from pub dev and run it this script:

Only create an ios native file if you want android look at this sample

3- Going to add the bridge file in the ios folder:

https://javedmultani16.medium.com/adding-a-swift-bridging-header-b6b0a7ab895f

This file needs to use swift code. Pigeon has to create objective-c code and We need to implement our custom code for this created code. After everything is done, you going to add this line to runner-binding.h

#import “pigeon.h” //Pigeon name come from sh script. You can modify this name whatever you like.

4- Write your native solution using auto-generated code.

Finally, go to your App delegate and add this code. (You have to add this line for every new pigeon API)

BookApiSetup(controller.binaryMessenger, SwiftPigeonPlugin())

5- Call it your code from the flutter side.

floatingActionButton: FloatingActionButton(onPressed: () async {final result = await BookApi().search('a');setState(() { bookItems = result;});

That’s it we have done implementation of native code with pigeon and I think that's so easy and we just focus on swift code without bridge or model transformation.

Settings Bundle

That ability comes from the native side and We can use it pretty easily. If we created our setting bundle file, we can update this file code and application settings. Okay, let’s think about what will we gain from this ability?

  • Default user option or token. (Especially, your team tester has a test account and needs to log in every time. After this bundle, your tester going to write token or user information in-app detail.)
  • Environment option dev, uat, prod (Tester can be change API options without new build)
  • Every multiple selection, slider, etc.(Please read this blog after you’ll understand what you have)

That’s done. We have completed the settings bundle setup. Let’s go implement custom logic after this implementation.

Before to began swift side coding, you have to add this code in your app delegate and then call it in the application method:

Configuration Class [Dart]

That scenario has created many functions. Especially, update configuration files codes need to flutter side. Of course, we need to read data when the application starts because we need to check data for operations.(login, theme, etc.)

As you can see, we have AppThemeResult and UserModel different by primitive types. Pigeon has support default type converter of primitive types but you know use enum or custom model should be used like this.

Enumeration usage:

enum AppThemes { dark, light }
class AppThemesResult {
String? errorMessage;
AppThemes? state;
}

Normal modal usage:

class UserModel {
final String? userName;
final String? userPassword;
UserModel({
this.userName,
this.userPassword,
});
}

Then your configuration class has to be like this.

Thus we’re finished to dart the first part, we’re gonna implement the last part after the finish native side. We need to call pigeon script by the way. Finally, it has to create a configuration. h and configuration.m file in the ios folder.

After the pigeon script works

So let’s move on native part for implementing Setting.bundle concept.

Setting Bundle Implementation

As we told how to implement objective c file for swift class on top of this. First of all, before the began native site, you have to add your objective c file in “Runner-Bridging-Header” file with name.

#import "GeneratedPluginRegistrant.h"#import "configuration.h"//generated file

Then We need the configuration swift class for our custom logic.

public class ConfigurationPigeonApi: NSObject, ConfigurationApi{...}

It’ll automatically bring custom methods and models. We can show what we did we gain after the pigeon. Normally we need to check how is it use for example integer value for dart and swift but this time that doesn’t need.

Look at more detail for other languages

We just focus our business logic on the native side and call it generated model. Whatever dart or swift side, you don’t think about model or primitive type integration by the way let’s check out model how is showing.

In that time, we implemented core lines after we need to fill these methods for our logic.

Firstly, I’m going to implement fetch theme options from configuration file.

Theme Toggle in configuration file

After I’m going to fill the fetch theme method from the swift side.

As you see, we already added identifier key for all item fields. Thus, we need to call these values with this key, and I’m making the BundleKeys in order to use.

private enum BundleKeys: String {
case version = "settings_server_version"
case versionEnvironment = "settings_server_environment"
case versionTheme = "settings_server_theme"
}

You can use this enum when you need new item.

The other point is the version. That’s so important because in general, we have dev and prod configuring our mobile apps and sometimes check both environments for some result then if the result has been done, that means we’re to publish a new version.

Multiple api environment section

That’s implementation very similar to theme fetch. We just call the identifier key and then check what is current select. Finally, return the user selection to the screen. In another way, I’m creating to custom enum for parsing environment type.

Then getting to fill the fetch version method with this enum:

That’s it, right now we can use the configuration version without any new build.

Lastly, I wanna implement a custom user name. Of course, you think for what? While the app using in testing mode, probably you have a demo account. At this moment we get to write this account after the implementation, you can not write again username and password. This app reads the data by application starts and goes to the related screen if has any data.

I’m going to add a group and two text fields in the Root settings file. You can look at below image for what I'm doing in plist file.

After let’s look at how to see your app in ios app settings. You have two fields so you can write your user credential to fields.

After that need to swift native code to fetch this data. I’m going to add two new bundle keys with the fetch user function.

case userName = "settings_server_username"
case userPassword = "settings_server_userpassword"

and that method will bring credential data.

That’s it, now we can be ready to use the flutter side. Probably, you remember we can implement the pigeon side and it generates dart and swift code so right now we completed the native side by the way we don’t need any new methods or models. You just call methods and you will gain the native power directly.

Please don’t forget to add your native app configuration initialize in app delegate.

Flutter Implementation

Our native config has ready, we just write our custom logic. I wanna show first business to theme dark and light. It is so easy to implement. You have ConfigurationApi class and call it fetchTheme method to understand what is the current state.

Actually, I’m just writing simple flutter code you can use with better state management solutions.

As you can see, We are fetching data with FutureBuilder while every app starting or life(pause ) changed. And let’s look at the result:

That’s cool :). Let’s look at other implementations. I need to check user credentials on the splash page. If I found the name and password, the page will be going the authentication page.

As you can see again, we just call the native bridge code and check the result. It going to the native side for work.

Yes, that’s implemented, and finally, we’re going to implement an API environment for different sources. I need to service call then I’m adding to vexana to manage network resources. After the install the network library, I’m creating to network instance by checking the current environment result.

If the device is in dev and beta mode, you can add: “https://fluttertr-ead5c.firebaseio.com/api/dev” also if the device is in staging mode, you can use a different point: “https://fluttertr-ead5c.firebaseio.com/api/dev”

And this results when the page is working:

Yep, that’s it. We completed a big mission about how to manage the swift and dart sides. We learned configuration manager and how to write native code with pigeon solution. It was so grateful I think and if you use pigeon, you don't think installation process while writing native code.

Of course, you need to show this option only to debug such like environment. You can read this answer for this reason.

Source code:

Thank you for reading 🌟
Have a great coding in your life!

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm

--

--

Veli Bacık
Flutter Community

We always change the world, so just want it. [OLD]Google Developer Expert Flutter & Dart, Gamer, Work More!