Search code examples
node.jsurl-rewritingurl-routingflutter-web

flutter web(hosted in nodejs) unable to navigate to other page manually when using setPathUrlStrategy (remove # from url)


I am building a flutter web to serve different pages individually, no internal navigation among pages, users access the page individually as you manually type in the url. I removed the # in url using setPathUrlStrategy (url_strategy: ^0.2.0). Each page will have various query parameters to the path and I use Uri.base.queryParameters["param1"] to grab the query parameters value. All these are working fine on my development localhost. But when deploy to production, I use nodejs to host my flutter web app as described in https://blog.logrocket.com/flutter-web-app-node-js/ . The web only able to load the landing page(e.g myweb.com/home), when I manually enter the url (e.g myweb.com/registration), I received error "Cannot GET /registration"

I have tried add in .htaccess (as below) as described here (unable to navigate in flutter web by changing url after removing # from the url) but that doesn't help at all.

RewriteEngine on 
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.html?path=$1 [NC,L,QSA]

I believe there is some kind of url rewrite I need to code at somewhere, in flutter web or in the nodejs that hosting the flutter web?

so how can I make this work?

here is the nodejs code to host the flutter web app (../build/web is the folder where the release version of flutter web)

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser'); 
var app = express();
  
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '../build/web')));

var http = require('http');
var port = normalizePort('10810');
app.set('port', port);

var server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    return val;
  }

  if (port >= 0) {
    return port;
  }

  return false;
}
 

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  console.log('Listening on ' + bind);
}

flutter web code:

 
import 'package:flutter/material.dart';
import 'package:get/get.dart'; 
import 'package:mycode/page/page_home.dart';
import 'package:mycode/page/page_reg.dart';
import 'package:mycode/page/page_status.dart';
import 'package:url_strategy/url_strategy.dart';
 

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  setPathUrlStrategy(); 
  runApp(const MyApp());
}
 

class MyApp extends StatelessWidget { 
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    String initRoute = PageReg.routeName; 
    return GetMaterialApp(
      navigatorKey: navigatorKey,
      debugShowCheckedModeBanner: false,
      title: 'EDDA Registration',
      initialRoute: PageHome.routeName,
      getPages: [
        GetPage(name: PageReg.routeName, page: () => PageReg()),
        GetPage(name: PageStatus.routeName, page: () => PageStatus()),
        GetPage(name: PageHome.routeName, page: () => PageHome()),
      ],
    ); 
  }
}

Solution

  • It appears as though your express server's routing has not been set up correctly. When you are trying to reach '/registration', your express server is attempting to do the routing rather than your Flutter/web app hence you get the Cannot GET /registration error message which I assume will also be returning a 404 status code as it doesn't exist.

    The easiest solution would be to set all GET requests from your express server to return the web app and handle the routing within that. Ideally, routing should be done server-side but in this case, you're attempting to handle it within your web app.

    Try adding this code after line 9 and restart your express app, this will return your Fluter web app on every GET request to your express server and then you can handle the routing within your web app (remember to add a 404/routing not found page within your Flutter web app too!)

    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, '../build/web')));
    app.get('*', (_, res) => {
      res.sendFile(path.resolve(__dirname, '../build/web/index.html'));
    });