こんなのを作る
And | ![]() |
![]() |
iOS | ![]() |
![]() |
Androidの方はintentでTwitterアプリに飛ばしてツイート、iOSはTwitterKitのサポート終了の件もあるのでUIActivityViewControllerを使ってツイートするようにしました。
デザインも一応AndroidとiOSで少し変えてみてます。
どうやってやるのか
タイトルに「Flutterで」と書きましたが、実はツイートの部分はFlutterのPlatform Channelsというものを使っていて、実装自体はそれぞれMainActivity/AppDelegateに書いています。
なので半分Platform Channelsの使い方を説明する記事になります。
Platform Channelsの実装方法については公式にも書いてあるのでそちらを読んでいただくとよりわかりやすいです。
Writing custom platform-specific code with platform channels
本題
流れとしては、
- MethodChannelというものを使って各プラットフォームと連携できるチャネルを作り、
- 各プラットフォーム側でチャネル経由でメソッドが呼ばれた時の準備をしておいて、
- Flutter側からチャネル経由でメソッドをinvokeする
感じになります。
それぞれ詳しく解説していきます。
MethodChannelの作成
MethodChannelというものを使って各プラットフォームと連携できるチャネルを作り、
これは簡単で、MethodChannelのインスタンスを作っておくだけです。
static const channel = const MethodChannel('com.kitoko552.flutter_post_tweet_example/tweet');
com.kitoko552.flutter_post_tweet_example/tweet
の部分はなんでもよくて、私はiOS出身なのでDispatchQueueのラベルと同じような感じかなと考えて上のような文字列を付与しましたが、これがベストプラクティスかはわかりません🤔
公式にも「we recommend prefixing the channel name with a unique ‘domain prefix’」と書いてあるのでそんなに間違ってはいないはずです。
各プラットフォーム側の準備
各プラットフォーム側でチャネル経由でメソッドが呼ばれた時の準備をしておいて、
これはAndroidならMainActivity、iOSならAppDelegateにそれぞれ実装することになります。
ただどちらもやることはツイートを行う部分以外は一緒で、特定のMethodChannelを監視して、メソッドがinvokeされた時にツイートする処理を走らせるようにするだけです。
Android
Androidはこんな感じになりました。
上にも書きましたが、私は元々iOSエンジニアでAndroidは軽く触ったことがある程度なのでほぼ公式のコードをコピペしていて、Twitterを開く処理はこちらを参考に書きました。
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.kitoko552.flutter_post_tweet_example/tweet";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, Result result) {
if (methodCall.method.equals("tweet")) {
openTwitter();
} else {
result.notImplemented();
}
}
}
);
}
private void openTwitter() {
Intent intent = new Intent(Intent.ACTION_VIEW);
String message= Uri.encode("This tweet is posted on Flutter app!");
intent.setData(Uri.parse("twitter://post?message=" + message));
startActivity(intent);
}
}
ちなみにプロジェクトディレクトリ配下にandroid
というディレクトリがあるので、そこから辿っていけばMainActivity.javaに辿りつけます。
iOS
iOSはこんな感じになりました。
iOSは得意や任せろと思ってworkspaceを開いたらObjective-Cでびっくりしましたが、私がFlutterのプロジェクトを作る時に設定しなかっただけっぽいのでご安心を。
設定すればSwiftで作られるはずです。
今回は途中でSwiftに変えるのが面倒だったのと、何かよくわからないエラーが起きるのが嫌だったのでObjcのまま書きました(Objc久しぶりに書いたので不備があったらすみません🙇)。
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"com.kitoko552.flutter_post_tweet_example/tweet"
binaryMessenger:controller];
__weak AppDelegate* wself = self;
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([call.method isEqualToString:@"tweet"]) {
[wself openActivity];
} else {
result(FlutterMethodNotImplemented);
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)openActivity {
UIActivityViewController* controller = [[UIActivityViewController alloc] initWithActivityItems:@[@"This tweet is posted on Flutter app!"] applicationActivities:NULL];
[self.window.rootViewController presentViewController:controller animated:true completion:NULL];
}
@end
やってることはAndroidのものと同じです。
違うのはツイートするまでの処理だけです。
Flutter側でメソッドをinvoke
Flutter側からチャネル経由でメソッドをinvokeする
こちらも簡単で、任意のタイミングでメソッドを呼ぶだけです。
上の実装で、AndroidもiOSも"tweet"というメソッド名の時にそれぞれの処理を呼ぶようにしているので、今回の場合は以下のようになります。
Future<void> _onPressedButton() async {
try {
await channel.invokeMethod('tweet');
} on PlatformException catch (e) {
print(e);
}
}
おわりに
コードはGitHubにあげているのでもし良かったら参考にしてください。
今回はデザインについては言及しませんでしたが、Android/iOSごとにデザインを変えるということも試験的にやっているので、大したことはしていませんが興味がある方はコードを読んでみてください。