hnvn / flutter_image_cropper

A Flutter plugin for Android and iOS supports cropping images
1k stars 392 forks source link

Migrate from js to dart:interop_js | Migrate from dart:html to web #489

Closed lukassgramm closed 6 months ago

lukassgramm commented 6 months ago

With flutter 3.22 it is possible to build with --wasm. Also, many packages conflict with older versions of js. Therefore I created a fork that migrates js to interop_js and dart:html to web. It seems to work

Links:

lukassgramm commented 6 months ago

To be reviewed Error where there are @JS:


The '@JS' annotation
of 'dart:js_interop' can only be used for static interop, through extension types or '@staticInterop' classes.
Try making this class an extension type or marking it as '@staticInterop'.
ViewPort class {
lukassgramm commented 6 months ago

Removing @anonymous should work and you should add in the @JS the name of what you use in the .js file

hnvn commented 6 months ago

@lukassgramm have you finished yet?

lukassgramm commented 6 months ago

I'm not finished. There is no way to test if it works. Every time I have to run flutter build web --wasm. I didn't have time to finish. I can't test it locally the package with an existing flutter project, I dont know

hnvn commented 6 months ago

It's OK, let me continue doing it

lukassgramm commented 6 months ago

@hnvn If you want to continue, go ahead but review what I changed, I am having problems in other packages with this migration as well

hnvn commented 6 months ago

It's tough than I thought 😬

hnvn commented 6 months ago

Hmm, I need to rewrite all of JS interop codes, so it makes me consider that it is time to switch to different JS library

lukassgramm commented 6 months ago

If it helps you @hnvn these are the changes I made to a project where you connect the wallet with Solana. Leaving out the Solana talk you can see what I changed.

JS example:

const init = () => {
    const Web3Modal = window.Web3Modal.default;
    const WalletConnectProvider = window.WalletConnectProvider.default;
    const ethers = window.ethers;
    let web3Modal;
    let provider;

    const providerOptions = {
        walletconnect: {
            package: WalletConnectProvider,
        },
    };

    async function fetchAccountData() {
        const wallet = new ethers.providers.Web3Provider(provider);
        const network = await wallet.getNetwork();
        const signer = wallet.getSigner();
        const selectedAccount = await signer.getAddress();
        const balance = await signer.getBalance();
        const res =  {
            "address": selectedAccount
        }

        return JSON.stringify(res);

    }

    async function refreshAccountData() {
        await fetchAccountData();
    }

    const onConnect = async () => {
        try {
            provider = await web3Modal.connect();
        } catch (e) {
            console.log("Could not get a wallet connection", e);
            return;
        }
        provider.on("accountsChanged", (accounts) => {
            fetchAccountData();
        });
        provider.on("chainChanged", (chainId) => {
            fetchAccountData();
        });
        provider.on("networkChanged", (networkId) => {
            fetchAccountData();
        });
        await refreshAccountData();
    }

    window._onConnect = onConnect;
    window._fetchAccountData = fetchAccountData;
    window._refreshAccountData = refreshAccountData;

}
window.onload = () => {
    init();
}

Flutter with js package:


@JS()
library js_interop;

import 'package:js/js.dart';
import 'dart:js_util';

@JS()
external _onConnect();

@JS()
external _fetchAccountData();

class WalletInteropService {
  onConnect() async {
    final promise = await _onConnect();
    final data = await promiseToFuture(promise);
  }

  fetchAccountData() async {
    final promise = await _fetchAccountData();
    final data = await promiseToFuture(promise);
    return data;
  }
}

Migration

Flutter with dart:js_interop

I made a wrap of the class that was previously in dart and put it in the javascript file, so the code would be more organized

import 'dart:js_interop';

@JS('window.WalletManager.onConnect')
external JSPromise onConnect();

@JS('window.WalletManager.fetchAccountData')
external JSPromise<JSString> fetchAccountData();

To use these functions on flutter I had to port all types from js to dart:

 final res = await onConnect().toDart;
 final fetch_account = await fetchAccountData().toDart;
 setState(() {
         wallet = jsonDecode(fetch_account.toDart); 
         // wallet['address']
 });

new js file (I had also managed to implement the stream):

const init = () => {
    const Web3Modal = window.Web3Modal.default;
    const WalletConnectProvider = window.WalletConnectProvider.default;
    const ethers = window.ethers;
    let web3Modal;
    let provider;

    const providerOptions = {
        walletconnect: {
            package: WalletConnectProvider,
        },
    };

    window.WalletManager = {
        onConnect: async () => {
            try {
                provider = await web3Modal.connect();
                const wallet = new ethers.providers.Web3Provider(provider);
                const signer = wallet.getSigner();

                provider.on("accountsChanged", (accounts) => {
                    console.log("accountsChanged", accounts);
                    window.postMessage(JSON.stringify({ type: 'accountsChanged', address: accounts[0] }), "*");
                });

                provider.on("chainChanged", (chainId) => {
                    window.postMessage(JSON.stringify({ type: 'chainChanged', chainId: chainId }), "*");
                });

                provider.on("networkChanged", (networkId) => {
                    window.postMessage(JSON.stringify({ type: 'networkChanged', networkId: networkId }), "*");
                });
            } catch (e) {
                console.log("Could not get a wallet connection", e);
                window.postMessage(JSON.stringify({ type: 'error', message: e.message }), "*");
            }
        },
        fetchAccountData: async () => {
            const wallet = new ethers.providers.Web3Provider(provider);
            const network = await wallet.getNetwork();
            const signer = wallet.getSigner();
            const selectedAccount = await signer.getAddress();
            const balance = await signer.getBalance();

            const res = {
                "address": selectedAccount,
                "balance": balance.toString()
            };

            return JSON.stringify(res);
        },
        refreshAccountData: async () => {
            return await WalletManager.fetchAccountData();
        }
    };
}

window.onload = init;

This is listening to the stream in dart if it helps you:


import 'package:web/web.dart' as web;

//...

  void startListening() {
    web.window.onMessage.listen((event) {
      try {
        if (event.data == null) return;
        if (event.data.toString() == "[object Object]") return;
        final data = json.decode(event.data.toString());
        if (data['type'] == "accountsChanged") {
          setState(() {
            wallet['address'] = data['address'];
            print("Wallet Address: >${wallet['address']}<");

          });
        } else{
          print("Data: $data");
        }
      } catch (e) {
        print("Errore Listen: $e");
      }
    });
  }