カーソルを検知してテキストに下線をつける【Flutter Web】 / how to detect mouse hover on text and then make text with underline.
EDIT : 2021/6/16
Flutter 2.2からTextSpanのパラメータにonEnter
/onExit
パラメータが追加されました。これでマウスホバー時のテキストの制御ができます。。。
ベンリィ 😊
必要な情報
- どうやってマウスホバーを検知するか?/ how to detect mouse hover on flutter web?
ホバー検知のWidgetがリリースされたのが一ヶ月前とからしい。タイミング良かった :)
- テキストに下線をつけるには?/ how to get text with underline?
そしてこんなかんじで、対象のテキストへのマウスホバー時のハンドラをonEnter
にあてる
マウスホバーが外れた時のハンドラはonExit
boolのコントローラーで状態管理。ホバー時にはTextDecoration.underline
をあてる。
class MouseHoverDetectableText extends HookWidget { const MouseHoverDetectableText(this.text); final String text; @override Widget build(BuildContext context) { final hover = useState(false); return GestureDetector( child: MouseRegion( onEnter: (_) => hover.value = true, onExit: (_) => hover.value = false, child: Text( text, style: TextStyle( decoration: hover.value ? TextDecoration.underline : null, color: Colors.grey, fontSize: 12, ), ), ), onTap: () {}, ); } }
動かすとこんな感じになる 😊
FontWeight, Colorの変化などもこれ使えば簡単そうですね。
Twitter APIを触る 【Recent Search/Trends】/ dabbling with Twitter API 【Dart】
Twitter API
Twitter API docs -> https://developer.twitter.com/en/docs/twitter-api
あまり人気のないTwitter APIですが、触ってみようと思います。
今回は Recent Search/Trends の2つの機能です。
Twitter API key取得に関しては下記のブログを参考にしました。 意外とすんなり取得できたのでやってみてください!
Recent Search
- GET /2/tweets/search/recent
- cached_network_image
ニュースアプリとかで、【Twitterの反応】みたいなロジック、よく見ますよね? こんなの。
...... ところで、
みんなのガッキーがお嫁に行きましたね ;)
おめでとう!ガッキー!!!!!!!
Twitterキーワード検索のなんちゃってクライアントアプリみたいなものを作ります。
通常は
という流れだと思うのですが、今回はフロントでAPI叩いて整形しています。
センシティブなアカウントの情報を取得しようとすると、パラメータを設定しても、nullが帰ってくるようですね。;)
下記のようにほしいデータをクエリに仕込んでUriオブジェクトを作成します。ほしいデータはenumでまとめました。
userID、 tweetID、 名前、ユーザーネーム(@以下)、Tweet本文、いいね数、リツイート数、リプライ数、アイコンのurlくらいあればそれっぽく作れるはずです。
final url = Uri.https( 'api.twitter.com', '/2/tweets/search/recent', <String, dynamic>{ 'query': searchWords, 'expansions': RecentSearchParams.authorId.asString, 'tweet.fields': [ RecentSearchParams.createdAt.asString, RecentSearchParams.text.asString, RecentSearchParams.publicMetrics.asString, ].join(','), 'user.fields': [ RecentSearchParams.id.asString, RecentSearchParams.name.asString, RecentSearchParams.userName.asString, RecentSearchParams.profileImageUrl.asString, ].join(','), 'start_time': yesterday, }, );
先程のUriと認証情報を仕込んでhttp GETします。
final result = await http.get( url, headers: {'Authorization': 'Bearer ${oauthToken.accessToken}'}, ).catchError((dynamic e) { print('ERROR:$e'); });
あとはかえってきたjsonをほぐして、欲しいデータを抜き出します。
いろいろいじったところ、ざっくり下記のようになりました。
Twitter Trends by Location and Webview
- GET trends/place
- flutter_web_view
Trendsのほうは設定できるパラメータはほとんどありません。
必須のパラメータはwoied
です。
- WOIED(Where On Earth IDentifier) 32ビットの一意の参照識別子、らしいです。
これで地域を指定します。 Recent Search 同様にUriオブジェクトを作成します。パラメータに受け取ったwoiedをあてます。
final url = Uri.https( 'api.twitter.com', '/1.1/trends/place.json', <String, dynamic>{ 'id': woied, }, );
Trends画面の流れとしては、地域を選択して、上位50のトレンドを取得、タップするとそのトレンド検索の画面(webview)に移行します。
Trendsのほうはこんなかんじで、地域選択してトレンドをタップするとwebview画面に移行します。 webview機能はそこまで充実していない感じですが、表示するだけとか、表示してひと手間とかだったら、さっくりいけそうな感じはしますね :)
以上の内容はGitHubに公開してあります。:)
アプリからGDrive上にフォルダ作成、ファイルをアップロードする 【Google Drive API v3】
開発中のアプリでデータをCSV出力する機能を実装しました。出力先はGoogle Driveのアプリ専用フォルダです。
流れとしては、
- csvString作成
- 出力先のフォルダがGDrive上にあるか確認
- (なければ、フォルダ作成)
- csvファイルをGDrive上にアップロード
という感じになります。
csvStringの作成はいろいろな方法がありますが、僕はアプリ内のデータオブジェクトを下記のパッケージでcsvStringへ変換しました。 https://pub.dev/packages/csv
また、ここで記述した内容は API権限スコープ https://www.googleapis.com/auth/drive.file
のもとで行っています。
フォルダがあるかチェック -> ここでいう id
はファイルやフォルダ固有のIDのことです。ファイルやフォルダ作成時にAPIから返ってくるIDを保存しておき、次回以降はそのフォルダIDを指定してやり、アプリフォルダ以下にファイルを作成していきます。
アカウント変更などの影響で,IDは持っているが現在参照しているアカウント上にはフォルダが存在しない場合があるので、フォルダ名とIDでチェックします。
Future<bool> fileExistsOnGDrive(String fileName, String id) async { final client = GoogleHttpClient(await googleSignInAccount.authHeaders); final drive = googleApis.DriveApi(client); await drive.files .list(spaces: 'drive', $fields: 'files(id)', q: 'name = $fileName') .then((value) { if (value != null && value.files.length > 0) { for (var file in value.files) { if (file.id == id) { return true; } } } }); return false; }
list
でとれる情報は上記スコープのもとでは、アプリが作成したファイル/フォルダのみになります。
list
メソッドの q
パラメータ作成について -> https://developers.google.com/drive/api/v3/ref-search-terms#drive_properties
アプリフォルダ作成
final googleApisFolder = googleApis.File(); googleApisFolder.name = csvFolderName; googleApisFolder.mimeType = 'application/vnd.google-apps.folder'; // フォルダとして指定 final response = await drive.files.create(googleApisFolder); final String folderID = response.id; await setString(key, folderID); // 以降は保存したIDを指定して,フォルダ以下にファイルを作成
Google DriveにおけるmimeType
について -> https://developers.google.com/drive/api/v3/mime-types
アップロード
final csvFileToUpload = googleApis.File(); final csvFileOnLocal = File(csvPath); csvFileToUpload.parents = [folderID]; // 保存しておいたフォルダIDを指定する。 csvFileToUpload.name = path.basename(csvPath); final response = await drive.files.create( csvFileToUpload, uploadMedia: googleApis.Media(file.openRead(), file.lengthSync()), );
以上がざっくりとしたフォルダ作成 -> ファイルアップロードの流れでした。
list
やcreate
のパラメータをもっと読むと、さらに便利な使い方、リファクタが可能かと思います。
Google Drive API Docsを初めて見たときは情報量に圧倒されましたが、丁寧にわかりやすく、かつ情報を網羅しており(docsなのであたりまえ?)、非常にためになりました。
参考 :
drive.files.list
-> https://developers.google.com/drive/api/v3/reference/files/list
drive.files.create
-> https://developers.google.com/drive/api/v3/reference/files/create
Shared Preferencesのファイルはどこにあるのか? / Where are Shared Preferences Stored in 【Flutter/Android】?
開発中のアプリにおいて、userの入力したデータをすべてひっくるめて、Google Driveにバックアップする機能を実装しようとしています。
そのなかでShared Preferencesのデータがどこに保存されているのか確認する必要がありました。調べていくと下記のSO記事を見つけました。
いわく、/data/data/PACKAGE NAME/shared_prefs/FlutterSharedPreferences.xml
Androidの場合はここにあるよ、と。確かめてみます。
適当なデータをShared Preferences経由で保存し、下記のようにデータにアクセスします。
final file = File( '/data/data/MY PACKAGE NAME/shared_prefs/FlutterSharedPreferences.xml'); String contents = await file.readAsString(); if (contents != null) print( 'This is /data/data/MY PACKAGE NAME/shared_prefs/FlutterSharedPreferences.xml -> ...\n$contents'); else print('its NULL');
ありました!!!StringList形式と通常のStringのデータがあります。 同様にStethoパッケージを使って、Chrome Dev Toolsでも確認をしてみます。はい、もちろん同様のデータが確認できます。StringListのデータだけ、なんか不明な文字列に変換されている仕様なんですよね。最初見たときは、なんか文字化けしてるとおもいました。 :p
SHA-1 Key取得の方法 / How to get SHA-1 Key on【Win10】
※一番下にGoogle Developers公式のSHA-1 Key確認方法があります。それでつまづいたら、下記の内容を順に確認してみてください :)
FlutterとFireBase連携の際にSHA-1 Keyが必要になりました。備忘録として残します。
VScodeで開発をしているのですが、ちょっと手こずりました。 最初は下記の記事を参考に進めました。
stackoverflow.com
gladlew signingReport
をプロジェクトのディレクトリでRunさせて、とあります。
ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
と怒られました。
うん、JavaはJDKのダウンロードをしていない。 次に見つけたのが下記のYouTube動画です。
www.youtube.com
keytool -list -v -keystore C:\Users\USER NAME\.android\debug.keystore -alias androiddebugkey -storepass android -keypass android
このコマンドをC:\Users\USER NAME
で走らせるようです。
しかしkeytool
コマンドが認識されません。
そんなこんなで、keytoolが認識されない問題で、下記のSO記事にたどり着きました。
stackoverflow.com
C:\Program Files\Java\jdk1******\bin
から走らせて、とのことです。JDKをダウンロードして、走らせます。
コマンド keytool は見つかりませんでしたが、現在の場所に存在します。Windows PowerShell は、既定では、現在の場所からコマンドを読み込みません。このコマンドを信頼する場合は、".\keytool" と 入力してください
そうします。
不正なオプション: ...NAME\.android\debug.keystore
pathが正しく認識されていません。"..."
で囲います。....いけました😊
EDIT:
FireBaseのAndroid登録の画面でTipsとしてSHA-1 Keyの確認方法が詳しく出ていました。下記のリンクです。これが一番わかり易いですね!
インジケータつきのカルーセルスライダー/Carousel Slider with Indicator 【Flutter】
カルーセルスライダーにインデックスインジケータ(?)を実装したい。
開発中のカルーセルスライダーをいじる機会があったので記録に残します。 下記のパッケージはすごく充実していて、お手軽にカルーセルを実装できます。
上記のデモの実装は下記になります。インジケータ部分において、現在表示されていもののインデックスが黒丸で表現されています。
class _CarouselWithIndicatorState extends State<CarouselWithIndicatorDemo> { int _current = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Carousel with indicator demo')), body: Column( children: [ CarouselSlider( items: imageSliders, options: CarouselOptions( autoPlay: true, enlargeCenterPage: true, aspectRatio: 2.0, onPageChanged: (index, reason) { setState(() { _current = index; }); } ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: imgList.map((url) { int index = imgList.indexOf(url); return Container( width: 8.0, height: 8.0, margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0), decoration: BoxDecoration( shape: BoxShape.circle, color: _current == index ? Color.fromRGBO(0, 0, 0, 0.9) : Color.fromRGBO(0, 0, 0, 0.4), ), ); }).toList(), ), ] ), ); } }
グレーや黒の丸で表現されている上記のインジケータをいじり、インデックスの数字で表現をしたいと思います。 上記コードのインジケータ実装部ではRow()の子要素としてContainer()で丸を表現したものを返しているだけでしたが、下記のようにStackでまとめて、丸の上からIndexの数字を重ねてやります。
Row( mainAxisAlignment: MainAxisAlignment.center, children: imgList.map((url) { int index = imgList.indexOf(url); return Stack(children: [ Container( width: 36, height: 36, margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0), decoration: BoxDecoration( shape: BoxShape.circle, color: _current == index ? Colors.blue : Colors.grey[350], ), ), Container( margin: EdgeInsets.fromLTRB(2, 13, 2, 7), width: 36, height: 36, child: Text('${index + 1}', textAlign: TextAlign.center, style: TextStyle( color: Colors.black, fontSize: 24, fontWeight: FontWeight.bold))) ]); }).toList(), )
こんなかんじで実装できました☺
下記のレポジトリにあるので、見てやってください😊 github.com