Skip to content

A Simple Example: Single Form Widget with A Stream

Eray Erdin (&mut self) edited this page Jun 19, 2023 · 2 revisions

ℹ️ You can see the full implementation of StreamingSwitch and more here.

Let's make a switch widget that responds to the changes in real time using streams. In order to do this, we need StreamingSwitch and StreamController. StreamController is already in the built-in Dark SDK.

ℹ️ The name of every streaming form widget starts with Streaming*.

⚠️ Every Streaming* form widget requires you to use StreamController.

Let's assume we have a page as below:

class MySwitchPage extends StatelessWidget {
  const MySwitchPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            const Text('Switch value is not yet known.'), // we will update this text in realtime
            Switch( // this is just a regular built-in switch, we will change this
              value: true,
              onChanged: (val) {},
            ),
          ],
        ),
      ),
    );
  }
}

First, we need to change this into a StatefulWidget.

class MySwitchPage extends StatefulWidget {
  const MySwitchPage({super.key});

  @override
  State<MySwitchPage> createState() => _MySwitchPageState();
}

class _MySwitchPageState extends State<MySwitchPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            const Text('Switch value is not yet known.'),
            Switch(
              value: true,
              onChanged: (val) {},
            ),
          ],
        ),
      ),
    );
  }
}

In _MySwitchPageState, we will initialize and properly dispose a StreamController<bool>.

class _MySwitchPageState extends State<MySwitchPage> {
  // here we initialize our StreamController
  final StreamController<bool> _controller = StreamController();

  @override
  void dispose() {
    // here we need to properly close it so it doesn't leak resources
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            const Text('Switch value is not yet known.'),
            Switch(
              value: true,
              onChanged: (val) {},
            ),
          ],
        ),
      ),
    );
  }
}

⚠️ If you'd like to listen to StreamController in multiple locations in codebase, use StreamController.broadcast() to initialize it.

And we will use StreamingSwitch and attach our StreamController to it:

class _MySwitchPageState extends State<MySwitchPage> {
  final StreamController<bool> _controller = StreamController();

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            const Text('Switch value is not yet known.'),
            // here is the StreamingSwitch
            StreamingSwitch(
              controller: _controller,
              // initialValue: true,
            ),
          ],
        ),
      ),
    );
  }
}

Finally, we will replace Text with a StreamBuilder, which will listen to StreamController's stream and react on changes.

class _MySwitchPageState extends State<MySwitchPage> {
  final StreamController<bool> _controller = StreamController();

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            // replace text with `StreamBuilder` and listen to controller's stream
            StreamBuilder(
              stream: _controller.stream,
              builder: (context, snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.none:
                    return const Text('There is no stream yet.');
                  case ConnectionState.waiting:
                    return const Text('Waiting for stream connection...');
                  case ConnectionState.active:
                    return Text('Switch data: ${snapshot.data}');
                  case ConnectionState.done:
                    return const Text('Stream is completed.');
                }
              },
            ),
            StreamingSwitch(
              controller: _controller,
              // initialValue: true,
            ),
          ],
        ),
      ),
    );
  }
}
Clone this wiki locally