title: Flutter Stateful Widget State not Initializing
body: <p>I'm making a command and control application using Flutter, and have come across an odd problem. The main status page of the app shows a list of stateful widgets, which each own a WebSocket connection that streams state data from a connected robotic platform. This worked well when the robots themselves were hardcoded in. However now that I'm adding them dynamically (via barcode scans), only the first widget is showing status.</p>
  
  <p>Further investigation using the debugger shows that this is due to the fact that a state is <em>only</em> getting created for the first widget in the list. Subsequently added widgets are successfully getting constructed, but are <em>not</em> getting a state. Meaning that createState is not getting called for anything other than the very first widget added. I checked that the widgets themselves are indeed being added to the list and that they each have unique hash codes. Also, the IOWebSocketChannel's have unique hash codes, and all widget data is correct and unique for the different elements in the list.</p>
  
  <p>Any ideas as to what could be causing this problem?</p>
  
  <p>Code for the HomePageState:</p>
  
  <pre><code>class HomePageState extends State&lt;HomePage&gt; {
    String submittedString = "";
    StateContainerState container;
    List&lt;RobotSummary&gt; robotList = [];
    List&lt;String&gt; robotIps = [];
    final GlobalKey&lt;ScaffoldState&gt; scaffoldKey = new GlobalKey&lt;ScaffoldState&gt;();
  
    void addRobotToList(String ipAddress) {
      var channel = new IOWebSocketChannel.connect('ws://' + container.slsData.slsIpAddress + ':' + container.slsData.wsPort);
      channel.sink.add("http://" + ipAddress);
      var newConnection = new RobotSummary(key: new UniqueKey(), channel: channel, ipAddress: ipAddress, state: -1, fullAddress: 'http://' + container.slsData.slsIpAddress + ':' + container.slsData.wsPort,);
      scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text("Adding robot..."), duration: Duration(seconds: 2),));
      setState(() {
        robotList.add(newConnection);
        robotIps.add(ipAddress);
        submittedString = ipAddress;
      });
    }
  
    void _onSubmit(String val) {
  
      // Determine the scan data that was entered
      if(Validator.isIP(val)) {
        if(ModalRoute.of(context).settings.name == '/') {
          if (!robotIps.contains(val)) {
            addRobotToList(val);
          }
          else {
            scaffoldKey.currentState.showSnackBar(new SnackBar(
              content: new Text("Robot already added..."), duration: Duration(seconds: 5),));
          }
        }
        else {
          setState(() {
            _showSnackbar("Robot scanned. Go to page?", '/');
          });
        }
      }
      else if(Validator.isSlotId(val)) {
        setState(() {
          _showSnackbar("Slot scanned. Go to page?", '/slots');
        });
      }
      else if(Validator.isUPC(val)) {
        setState(() {
          _showSnackbar("Product scanned. Go to page?", '/products');
        });
      }
      else if (Validator.isToteId(val)) {
  
      }
    }
  
    @override
    Widget build(BuildContext context) {
      container = StateContainer.of(context);
      return new Scaffold (
        key: scaffoldKey,
        drawer: Drawer(
          child: CategoryRoute(),
        ),
        appBar: AppBar(
          title: Text(widget.topText),  
        ),
        bottomNavigationBar: BottomAppBar(
          child: new Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: &lt;Widget&gt;[
              IconButton(icon: Icon(Icons.camera_alt), onPressed: scan,),
              IconButton(icon: Icon(Icons.search), onPressed: _showModalSheet,),
            ],
          ),
        ),
        body: robotList.length &gt; 0 ? ListView(children: robotList) : Center(child: Text("Please scan a robot.", style: TextStyle(fontSize: 24.0, color: Colors.blue),),),
      );
    }
  
    void _showModalSheet() {
      showModalBottomSheet(
          context: context,
          builder: (builder) {
            return _searchBar(context);
          });
    }
  
    void _showSnackbar(String message, String route) {
      scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text(message),
        action: SnackBarAction(
          label: 'Go?', 
          onPressed: () {
            if (route == '/') {
              Navigator.popUntil(context,ModalRoute.withName('/'));
            }
            else {
              Navigator.of(context).pushNamed(route); 
            }
          },),
        duration: Duration(seconds: 5),));
    }
  
    Widget _searchBar(BuildContext context) {
      return new Scaffold(
        body: Container(
        height: 75.0,
        color: iam_blue,
        child: Center(
        child: TextField(
          style: TextStyle (color: Colors.white, fontSize: 18.0),
          autofocus: true,
          keyboardType: TextInputType.number,
          onSubmitted: (String submittedStr) {
            Navigator.pop(context);
            _onSubmit(submittedStr);
          },
          decoration: new InputDecoration(
          border: InputBorder.none,
          hintText: 'Scan a tote, robot, UPC, or slot',
          hintStyle: TextStyle(color: Colors.white70),
          icon: const Icon(Icons.search, color: Colors.white70,)),
        ),
      )));
    }
  
    Future scan() async {
      try {
        String barcode = await BarcodeScanner.scan();
        setState(() =&gt; this._onSubmit(barcode));
      } on PlatformException catch (e) {
        if (e.code == BarcodeScanner.CameraAccessDenied) {
          setState(() {
            print('The user did not grant the camera permission!');
          });
        } else {
          setState(() =&gt; print('Unknown error: $e'));
        }
      } on FormatException{
        setState(() =&gt; print('null (User returned using the "back"-button before scanning anything. Result)'));
      } catch (e) {
        setState(() =&gt; print('Unknown error: $e'));
      }
    }
  }
  </code></pre>
  
  <p>Code snippet for the RobotSummary class:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:meta/meta.dart';
  import 'package:test_app/genericStateSummary_static.dart';
  import 'dart:convert';
  import 'package:web_socket_channel/web_socket_channel.dart';
  import 'package:test_app/StateDecodeJsonFull.dart';
  import 'dart:async';
  import 'package:test_app/dataValidation.dart';
  
  class RobotSummary extends StatefulWidget {
    final String ipAddress;
    final String _port = '5000';
    final int state;
    final String fullAddress;
    final WebSocketChannel channel;
  
    RobotSummary({
      Key key,
      @required this.ipAddress,
      @required this.channel,
      this.state = -1,
      this.fullAddress = "http://10.1.10.200:5000",
    }) :  assert(Validator.isIP(ipAddress)),
          super(key: key);
  
    @override
    _RobotSummaryState createState() =&gt; new _RobotSummaryState();
  }
  
  class _RobotSummaryState extends State&lt;RobotSummary&gt; {
    StreamController&lt;StateDecodeJsonFull&gt; streamController;
  
    @override
    void initState() {
      super.initState();
      streamController = StreamController.broadcast();
    }
  
    @override
    Widget build(BuildContext context) {
  
      return new Padding(
        padding: const EdgeInsets.all(20.0),
        child: new StreamBuilder(
          stream: widget.channel.stream,
          builder: (context, snapshot) {
            //streamController.sink.add('{"autonomyControllerState" : 3,  "pickCurrentListName" : "69152", "plannerExecutionProgress" : 82,   "pickUpcCode" : "00814638", "robotName" : "Adam"}');
            return getStateWidget(snapshot);
          },
        ),
      );
    }
  
    @override
    void dispose() {
      streamController.sink.close();
      super.dispose();
    }
  }
  </code></pre>