5
5

More than 5 years have passed since last update.

【Flutter】Chromeブラウザの「共有」から表示している「ページ名」と「URL」を取得する

Last updated at Posted at 2018-10-07

※今のところAndroidアプリだけでしか実現できていません

9/19に開催された
Dart/Flutter入門者向けハンズオン + もくもく会
に行ってからちょっとだけDart/Flutter熱が上がったので記事書いてみました。

がっつりネイティブモバイルアプリを作ったことがなかったので、
ManifestファイルとかAndroidのライフサイクルで結構ハマりました。
そしてGradleについては謎のまま。
要学習・・・

やりたかったこと

掲題の通りです。
「Flutterで作ったアプリでChromeブラウザの「共有」から表示中のWebサイトの「ページ名」と「URL」を取得して表示させる」
です。

Screenshot_1538928516.png

作ったもの

こんな感じです。

share_reciver.gif

やったこと

環境構築とかその辺は省略してます

基本的に公式の What is the equivalent of an Intent in Flutter? の沿ったことをやっています。

AndroidManifest.xmlを編集

Chromeブラウザの「共有」に自分のアプリが表示されるようにする

<intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

上記をMainActivityの設定に追加します。
Chromeから「共有」すると INTENT.ACTION_SEND なるものが発行されるので、
それを受け取れるようになります。

Activityが複数起動しないようにする

<activity            
    android:launchMode="singleTask"
    android:name=".MainActivity"/>

上記のようにActivityの設定に android:launchMode="singleTask" を追記しました。
インテントを受け取るたびにonCreateが実行されてしまうとActivityがその分だけ作られてしまっていて困っていたのですが、ひとまずこれで解決できました。
ただ、調べていると「SingleTask 非推奨」なるキーワードに遭遇しておりビビっているので、これでいいのかは調査が必要。

MainActivity.javaを編集

Chromeから発行された INTENT.ACTION_SEND を受け取ってDart側に渡す準備

必要なことは以下のなります。

  • URLとタイトルを保持するメンバ変数を用意
  • DartコードとやりとりするためのMethodChannelを用意
  • Intentを受け取り、中身(ページ名、URL)を取り出す処理を用意

これらを全部盛りするとMainActivity.javaをこんな感じです。

import android.content.Intent;
import android.os.Bundle;

import java.util.HashMap;
import java.util.Map;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {

    // URLとタイトルを保持するメンバ変数を用意
    private String url;
    private String title;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);

    // DartコードとやりとりするためのMethodChannelを用意
    new MethodChannel(getFlutterView(), "app.channel.webpage.info").setMethodCallHandler(new MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
            if (methodCall.method.contentEquals("getWebpageInfo")) {

                Map<String, String> webpage = new HashMap<>();
                webpage.put("url", url);
                webpage.put("title", title);

                result.success(webpage);

                url = null;
                title = null;
            }
        }
    });
  }

  @Override
  protected void onNewIntent(Intent intent) {
      super.onNewIntent(intent);
      intentHandler(intent);
  }

  // Intentを受け取り、中身(ページ名、URL)を取り出す処理を用意
  private void intentHandler(Intent intent) {
      String action = intent.getAction();
      String type = intent.getType();
      if (Intent.ACTION_SEND.equals(action) && type != null) {
          if ("text/plain".equals(type)) {
            Bundle bundle = intent.getExtras();
            this.url = intent.getStringExtra(Intent.EXTRA_TEXT);
            this.title = intent.getStringExtra(Intent.EXTRA_SUBJECT);
          }
      }
  }

}

main.dartの編集

必要なことは以下になります

  • Activityのライフサイクルで指定処理を実行できるようにする
  • Java(MainActivity.java)からデータをもらう
  • 受け取ったデータを画面に表示

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}


class _MyAppState extends State<MyApp>
    // Activityのライフサイクルで指定処理を実行できるようにする①
    with WidgetsBindingObserver {

  // Java(MainActivity.java)からデータをもらう①
  static const channel = const MethodChannel('app.channel.webpage.info');

  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final TextEditingController _urlController = new TextEditingController();
  final TextEditingController _titleController = new TextEditingController();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  // Activityのライフサイクルで指定処理を実行できるようにする②
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      getWebpageInfo();
    }
  }

  void getWebpageInfo() async {

    // Java(MainActivity.java)からデータをもらう②
    var message = await  channel.invokeMethod('getWebpageInfo');
    var webpageInfo = WebpageInfo.fromMap(message);

    // 受け取った値を画面に表示
    setState(() {
      _urlController.value = TextEditingValue(text: webpageInfo.url);
      _titleController.value = TextEditingValue(text: webpageInfo.title);
    });
  }

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('「共有」から❤️'),
        ),
        body: Center(
          child: Container(
            padding: EdgeInsets.all(20.0),
            child: Form(
              key: this._formKey,
                child: ListView(
                children: <Widget>[
                  TextFormField(
                      controller: _urlController,
                      keyboardType: TextInputType
                          .url, // Use email input type for emails.
                      decoration: InputDecoration(
                          hintText: 'url', labelText: 'Share URL')),
                  TextFormField(
                      controller: _titleController,
                      keyboardType: TextInputType.text,
                      decoration: InputDecoration(
                          hintText: 'page title', labelText: 'Title')),
                  Container(
                    child: new RaisedButton(
                      child: new Text(
                        'Save',
                        style: new TextStyle(color: Colors.white),
                      ),
                      onPressed: () => null,
                      color: Colors.blue,
                    ),
                    margin: new EdgeInsets.only(top: 20.0),
                  )
                ]
              )
            ),
          )
        ),
      ),
    );
  }
}


// WebpageInfoモデル
class WebpageInfo {

  WebpageInfo._({
    this.url,
    this.title
  });

  String url = '';
  String title = '';

  static WebpageInfo fromMap(dynamic message) {
    final Map<dynamic, dynamic> map = message;
    return WebpageInfo._(
      url: map['url'],
      title: map['title']
    );
  }
}

こんな感じでした。

感想

プラグイン化してみたいな〜。
iOSは何をどうすればいいかわからないな〜。

Androidもよくわかってないしな〜。

そのうちGitHubに公開すると思います。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5