Friday, February 25, 2022

[SOLVED] Flutter HTTP Post Request does not reach Node-RED on Raspberry Pi

Issue

I am trying to send data from Flutter to Node-RED (to Raspberry Pi) over HTTP (not HTTPS). The URL http://bierbrauserver.ddns.net is from NO-IP (DDNS). The transfer to a test server from a Youtube Tutorial works fine. But when I change the URL from "https://reqres.in/api/users" to "http://bierbrauserver.ddns.net:1880" I get no input in the debug window of Node-RED. I tried in the AndroidManifest.xml to add the entry so that http is allowed, but that didn't solve the problem either. Furthermore I checked the port forwarding in the WLAN router configuration of the network where the Raspberry Pi is located. Here a port forwarding of HTTP and 1880 (Node-RED) was configured. The connection of the IP addresses should be ok, because I can access a database from an external port, which is also located on the Raspberry Pi. Can it be that only httpS post requests are possible ?

Future<UserModel> createUser(String name, String jobTitle)async{

const String Url = "https://reqres.in/api/users";
// const String Url = "https://bierbrauserver.ddns.net:1880";

final response = await http.post(Uri.parse(Url),body:
{
  "name": name,
  "job": jobTitle
});
if (response.statusCode == 201) {
  final String responseString = response.body;
  return userModelFromJson(responseString);
}
else
  {
    print(response.statusCode);
    return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
  }
}

Thank you for your help.


Solution

The problem is solved. The program code has already been modified because I forgot to answer the question immediately when the problem was fixed. The problem was hidden in the Node-RED program. The wrong Node was selected in Node-RED. The URL was extended with "/test", which is reused in Node-RED in the "http in" node. Now everything works fine.

For your information: "Zutat_1", "Zutat_2", "Zutat_3" and "Zutat_4" are global variables in this program and therefore do not have to be passed to the "createRezept" function. Additionally, it should be mentioned that some elements in the code that do not belong to the English language are executed in the German language.

Future<RezeptModel> createRezept()async{
   const String Url = "http://bierbrauserver.ddns.net:1880/test";

    final response = await http.post(Uri.parse(Url),body:
    {
      "zutat1": Zutat_1.toStringAsFixed(2),
      "zutat2": Zutat_2.toStringAsFixed(2),
      "zutat3": Zutat_3.toStringAsFixed(2),
      "zutat4": Zutat_4.toStringAsFixed(2),
      "createdAt" : DateTime.now().toIso8601String()

    });
    if (response.statusCode == 201) {
      final String responseString = response.body;
      return rezeptModelFromJson(responseString);
    }
    else
      {
        print(response.statusCode);
        return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
      }
}

Here is the code which is used for parsing.(This is in a separate file, which in my case is named "Rezept_model.dart" )

import 'dart:convert';

RezeptModel rezeptModelFromJson(String str) => RezeptModel.fromJson(json.decode(str));

String rezeptModelToJson(RezeptModel data) => json.encode(data.toJson());

class RezeptModel {
    RezeptModel({
       required this.zutat1,
       required this.zutat2,
       required this.zutat3,
       required this.zutat4,
       required this.createdAt,
    });

    String zutat1;
    String zutat2;
    String zutat3;
    String zutat4;
    DateTime createdAt;

    factory RezeptModel.fromJson(Map<String, dynamic> json) => RezeptModel(
        zutat1: json["zutat1"],
        zutat2: json["zutat2"],
        zutat3: json["zutat3"],
        zutat4: json["zutat4"],
        createdAt: DateTime.parse(json["createdAt"]),
    );

    Map<String, dynamic> toJson() => {
        "zutat1": zutat1,
        "zutat2": zutat2,
        "zutat3": zutat3,
        "zutat4": zutat4,
        "createdAt": createdAt.toIso8601String(),
    };
}

For completeness here is the whole code of the main program.

import 'dart:convert';
import 'dart:math';

import 'package:bier_brau_project/variables.dart';
import 'package:bier_brau_project/rezept_model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {

  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bier Brau App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'HTTP Test Site'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;


  @override
  State<MyHomePage> createState() => _MyHomePageState();
}


 Future<RezeptModel> createRezept()async{
   const String Url = "http://bierbrauserver.ddns.net:1880/test";

    final response = await http.post(Uri.parse(Url),body:
    {
      "zutat1": Zutat_1.toStringAsFixed(2),
      "zutat2": Zutat_2.toStringAsFixed(2),
      "zutat3": Zutat_3.toStringAsFixed(2),
      "zutat4": Zutat_4.toStringAsFixed(2),
      "createdAt" : DateTime.now().toIso8601String()

    });
    if (response.statusCode == 201) {
      final String responseString = response.body;
      return rezeptModelFromJson(responseString);
    }
    else
      {
        print(response.statusCode);
        return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
      }
}


class _MyHomePageState extends State<MyHomePage> {

  RezeptModel _rezept = RezeptModel(zutat1: "init", zutat2: "init", zutat3: "init",zutat4: "init", createdAt: DateTime.now());


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children:  <Widget>[
             MySlider(Min:22.0,Max:40.0,Devisions: 100,unit:'l',title: 'Wasser',ID:1),
             MySlider(Min:4.0,Max:20.0,Devisions: 100,unit:'kg',title:'Malz',ID: 2,),
             MySlider(Min:60.0,Max:300.0,Devisions: 100,unit:'g',title:'Hopfen',ID: 3,),
             MySlider(Min:12.0,Max:60.0,Devisions: 100,unit:'g',title:'Hefe',ID:4),
             const SizedBox(height:32.0,),

            Text( "Wasser "+ _rezept.zutat1 +"\n"
                +"Malz "+_rezept.zutat2 +"\n"
                +"Mopfen "+ _rezept.zutat3 +"\n"
               +"Hefe "+ _rezept.zutat4 +"\n"
                +"ist Erfolgreich übertragen worden "
                + _rezept.createdAt.toIso8601String())


          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: ()async{
          final RezeptModel rezept= await createRezept();
          setState(() {
              _rezept=rezept;
          });
        },
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}










/// This is the stateful widget that the main application instantiates.
class MySlider extends StatefulWidget {
  MySlider({Key? key,required this.Min, required this.Max,required this.Devisions,required this.unit,required this.title,required this.ID}): super(key: key);

  final double Min;
  final double Max;
  final String unit;
  final String title;
  final int Devisions;
  final int ID;
  bool ValueChanged= false;


  @override
  State<MySlider> createState() => _MySliderState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _MySliderState extends State<MySlider> {

  double _currentSliderValue =0;

  @override
  Widget build(BuildContext context) {
    return Padding (
      padding: const EdgeInsetsDirectional.fromSTEB(32.0, 32.0, 32.0, 16.0),
        child: Column(children: <Widget> [
          Text(
            widget.title,
            style:  const TextStyle(
                color: Colors.black54,
                height: 1,
                fontSize: 18
            ),

          ),

          Slider(
         value: widget.ValueChanged== false ? ((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue,
          min: widget.Min,
          max: widget.Max,
          divisions: widget.Devisions,
          label: _currentSliderValue.toStringAsFixed(2),
          onChanged: (double value) {
            setState(() {
              _currentSliderValue = value;
              first_call = false;
              widget.ValueChanged =true;
              switch(widget.ID) {
                case 1: {Zutat_1 =double.parse(value.toStringAsFixed(2));}
                break;

                case 2: {Zutat_2 =double.parse(value.toStringAsFixed(2));}
                break;

                case 3: {Zutat_3 =double.parse(value.toStringAsFixed(2));}
                break;

                case 4: {Zutat_4 =double.parse(value.toStringAsFixed(2));}
                break;

                default: {}
                break;
              }

            });
          },
        ),
          Text(
              (widget.ValueChanged== false ?((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue.toStringAsFixed(2)).toString() + ' '+widget.unit,
          ),

        ])
        );
  }
}

Many thanks to all those who have dealt with this problem. Here are still two images deposited around the connections to clarify.

Node-RED program to receive from http post

Android Studio emulator with the test application



Answered By - Michael95
Answer Checked By - Candace Johnson (WPSolving Volunteer)