dengyin2000 / dynamic_widget

A Backend-Driven UI toolkit, build your dynamic UI with json, and the json format is very similar with flutter widget code.
Apache License 2.0
1.56k stars 309 forks source link

Widget fails to render when a Stack widget is used #93

Open loongyeat opened 2 years ago

loongyeat commented 2 years ago

As title implies. Console shows flutter: Null check operator used on a null value. To re-create:


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

  static final _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final exporter = _key.currentWidget as DynamicWidgetJsonExportor?;
          final exportedJsonString = exporter?.exportJsonString();

          if (exportedJsonString == null) {
            return;
          }

          Navigator.of(context).push<void>(
            MaterialPageRoute(
              builder: (context) => const RendererWidget(exportedJsonString),
            ),
          );
        },
        child: const Icon(Icons.copy),
      ),
      body: DynamicWidgetJsonExportor(
        key: _key,
        child: _buildSimpleWidget(),
      ),
    );
  }

  Widget _buildSimpleWidget() {
    return Stack(
      children: [
        Icon(Icons.star),
        Icon(Icons.abc),
      ],
    );
  }
}

class RendererWidget extends StatelessWidget {
  const RendererWidget(this.json, {Key? key}) : super(key: key);

  final String json;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(),
      body: SafeArea(
        child: FutureBuilder<Widget?>(
          future: _buildWidget(context),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              print(snapshot.error);
            }

            if (!snapshot.hasData) {
              return Container();
            }

            return SizedBox.expand(
              child: snapshot.data,
            );
          },
        ),
      ),
    );
  }

  Future<Widget?> _buildWidget(BuildContext context) async {
    return Future.delayed(const Duration(seconds: 1), () {
      return DynamicWidgetBuilder.build(
        json,
        context,
        DefaultClickListener(),
      );
    });
  }
}

class DefaultClickListener implements ClickListener {
  @override
  void onClicked(String? event) {
    print(event);
  }
}
dengyin2000 commented 2 years ago

As title implies. Console shows flutter: Null check operator used on a null value. To re-create:

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

  static final _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final exporter = _key.currentWidget as DynamicWidgetJsonExportor?;
          final exportedJsonString = exporter?.exportJsonString();

          if (exportedJsonString == null) {
            return;
          }

          Navigator.of(context).push<void>(
            MaterialPageRoute(
              builder: (context) => const RendererWidget(exportedJsonString),
            ),
          );
        },
        child: const Icon(Icons.copy),
      ),
      body: DynamicWidgetJsonExportor(
        key: _key,
        child: _buildSimpleWidget(),
      ),
    );
  }

  Widget _buildSimpleWidget() {
    return Stack(
      children: [
        Icon(Icons.star),
        Icon(Icons.abc),
      ],
    );
  }
}

class RendererWidget extends StatelessWidget {
  const RendererWidget(this.json, {Key? key}) : super(key: key);

  final String json;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(),
      body: SafeArea(
        child: FutureBuilder<Widget?>(
          future: _buildWidget(context),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              print(snapshot.error);
            }

            if (!snapshot.hasData) {
              return Container();
            }

            return SizedBox.expand(
              child: snapshot.data,
            );
          },
        ),
      ),
    );
  }

  Future<Widget?> _buildWidget(BuildContext context) async {
    return Future.delayed(const Duration(seconds: 1), () {
      return DynamicWidgetBuilder.build(
        json,
        context,
        DefaultClickListener(),
      );
    });
  }
}

class DefaultClickListener implements ClickListener {
  @override
  void onClicked(String? event) {
    print(event);
  }
}

I try your code, it seems it's ok. the "exportedJsonString" is

exportedJsonString: {"type":"Stack","alignment":"topStart","textDirection":"ltr","fit":"loose","clipBehavior":"hardEdge","children":[{"type":"Icon","data":"star","size":null,"color":null,"semanticLabel":null,"textDirection":null},{"type":"Icon","data":"ac_unit","size":null,"color":null,"semanticLabel":null,"textDirection":null}]}