ListView 在使用 Riverpod 提供程序时不渲染小部件。

huangapple go评论53阅读模式
英文:

ListView dosen't render widget when using riverpod provider

问题

我正在使用Flutter编写一个应用程序,该应用程序使用SimpleDialog将项目添加到列表中,并在添加项目时在ListView中显示这些项目。
问题是,只有当我切换到另一页然后再回到列表页面时,列表才会更新,所以数据已经存在。我只是不明白如何使列表重新渲染...

这是我声明的Provider:

@riverpod
List<Task> tasks(TasksRef ref) => [];

这是我的小部件如何设置列表(tasks):

class TasksScreen extends StatefulHookConsumerWidget {
  const TasksScreen({super.key});

  @override
  ConsumerState<TasksScreen> createState() => _TasksScreenState();
}

class _TasksScreenState extends ConsumerState<TasksScreen> {
  @override
  Widget build(BuildContext context) {
    List<model.Task> tasks = ref.read(tasksProvider);
    List<Task> list = [];
    list = [...test(tasks)];

    return ProviderScope(
        child: Scaffold(
      body: ListView.builder(
        itemBuilder: (context, index) {
          return Task(task: ref.read(tasksProvider)[index]);
        },
        itemCount: ref.read(tasksProvider).length,
      ),
    ));
  }

  List<Task> test(List<model.Task> list) {
    List<Task> tasks = [];

    for (var i = 0; i < list.length; i++) {
      tasks.add(Task(task: list[i]));
    }

    return tasks;
  }
}

非常感谢帮助!

我期望Provider的更改会导致列表重新渲染页面并显示列表。

英文:

I writing an app using flutter that add item to list (using simpleDialog) and show thus items in ListView when you add on.
The problem is that the list only update when I move to another page and them get to the list page, so the data is there. I just don't understand how to make the list to re-render...

This is the provider I declare

@riverpod
List&lt;Task&gt; tasks(TasksRef ref) =&gt; [];

This is my widget how preset the list (tasks)

class TasksScreen extends StatefulHookConsumerWidget {
  const TasksScreen({super.key});

  @override
  ConsumerState&lt;TasksScreen&gt; createState() =&gt; _TasksScreenState();
}

class _TasksScreenState extends ConsumerState&lt;TasksScreen&gt; {
  @override
  Widget build(BuildContext context) {
    List&lt;model.Task&gt; tasks = ref.read(tasksProvider);
    List&lt;Task&gt; list = [];
    list = [...test(tasks)];

    return ProviderScope(
        child: Scaffold(
      body: ListView.builder(
        itemBuilder: (context, index) {
          return Task(task: ref.read(tasksProvider)[index]);
        },
        itemCount: ref.read(tasksProvider).length,
      ),
    ));
  }

  List&lt;Task&gt; test(List&lt;model.Task&gt; list) {
    List&lt;Task&gt; tasks = [];

    for (var i = 0; i &lt; list.length; i++) {
      tasks.add(Task(task: list[i]));
    }

    return tasks;
  }
}

Thanks a lot for helpers !

I expecting that the changing of the provider lead to the list that render the page and show the list

答案1

得分: 1

你应该使用 watch 而不是 read,因为根据文档的描述,read 在状态更改时不会重新构建小部件。

class TaskListNotifier extends StateNotifier<List<model.Task>> {
    TaskListNotifier() : super([]); // 初始状态
    void add(model.Task) {
        state.add(model.Task);
        state = [...state]; // 这将通知并重建 UI
    }
}

final tasksProvider = StateNotifierProvider<TaskListNotifier, List<model.Task>>((ref) {
  return TaskListNotifier();
});

class TasksScreen extends StatefulHookConsumerWidget {
  const TasksScreen({super.key});

  @override
  ConsumerState<TasksScreen> createState() => _TasksScreenState();
}

class _TasksScreenState extends ConsumerState<TasksScreen> {
  @override
  Widget build(BuildContext context) {
    List<model.Task> tasks = ref.watch(tasksProvider);

    return ProviderScope(
        child: Scaffold(
      body: ListView.builder(
        itemBuilder: (context, index) {
          return GestureDetector(
            onTap: () {
              ref.read(tasksProvider.notifier).add(tasks[index]);
            },
            child: Task(task: tasks[index]),
          );
        },
        itemCount: tasks.length,
      ),
    ));
  }
}
英文:

You should use watch instead of read since read does not rebuild the widget when the state change as describe in the document

class TaskListNotifier extends StateNotifier&lt;List&lt;model.Task&gt;&gt; {
    TaskListNotifier() : super([]); // initial state
    void add(model.Task) {
        state.add(model.Task);
        state = [...state]; // this will notify and rebuild the UI
    }
}

final tasksProvider = StateNotifierProvider&lt;TaskListNotifier, List&lt;model.Task&gt;&gt;((ref) {
  return TaskListNotifier();
});

class TasksScreen extends StatefulHookConsumerWidget {
  const TasksScreen({super.key});

  @override
  ConsumerState&lt;TasksScreen&gt; createState() =&gt; _TasksScreenState();
}

class _TasksScreenState extends ConsumerState&lt;TasksScreen&gt; {
  @override
  Widget build(BuildContext context) {
    List&lt;model.Task&gt; tasks = ref.watch(tasksProvider);

    return ProviderScope(
        child: Scaffold(
      body: ListView.builder(
        itemBuilder: (context, index) {
          return GestureDetector(
          onTap: () {
            ref.read(tasksProvider.notifier).add(tasks[index]);
          },
          child: Task(task: tasks[index]),
          );
        },
        itemCount: tasks.length,
      ),
    ));
  }
}

答案2

得分: 0

抱歉,我以前没有使用过riverpod包,所以不能直接为您提供关于这个的建议,您可能需要等待其他答案。

但是对于您想要触发重建的构建方法,您应该使用watch而不是read

我不知道riverpod是否也有一些包装器或帮助类似于ChangeNotifier,您需要将列表包装在其中。

转换为默认的provider包,您的代码片段的工作示例如下:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'dart:collection';

class TaskStorage with ChangeNotifier {
  List<ModelTask> _myTasks = [];

  void add(ModelTask newTask) {
    _myTasks.add(newTask);
    notifyListeners();
  }

  UnmodifiableListView<ModelTask> get tasks => UnmodifiableListView<ModelTask>(_myTasks);
}

class ModelTask {
  int someData = 10;
}

class Task extends StatelessWidget {
  final ModelTask task;

  const Task({
    required this.task,
  });

  @override
  Widget build(BuildContext context) {
    return Text("task: ${task.someData}");
  }
}

class TasksScreen extends StatefulWidget {
  const TasksScreen({Key? key});

  @override
  State createState() => _TasksScreenState();
}

class _TasksScreenState extends State<TasksScreen> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: ListView.builder(
            itemBuilder: (context, index) {
              return Task(task: context.watch<TaskStorage>().tasks[index]);
            },
            itemCount: context.watch<TaskStorage>().tasks.length,
          ),
        ),
        ElevatedButton(
          onPressed: () => context.read<TaskStorage>().add(ModelTask()),
          child: Text("ADD"),
        ),
      ],
    );
  }
}

void main() => runApp(
      MaterialApp(
        home: Scaffold(
          backgroundColor: Colors.green,
          appBar: AppBar(),
          body: ChangeNotifierProvider<TaskStorage>(
            create: (BuildContext context) {
              return TaskStorage();
            },
            child: const TasksScreen(),
          ),
        ),
      ),
    );

也许这仍然可以帮助您。我建议查看riverpod包的文档。

英文:

Sorry, i have never used the riverpod package before, so i can't directly give you advice on this and you might have to wait for a different answer.

But for the build method where you want to trigger rebuilds, you should use watch instead of read.

And i don't know if riverpod also has some wrapper, or helper class like a ChangeNotifier that you need to wrap your list in.

Converted to the default provider package, a working example for your code snippet would look like:

import &#39;package:flutter/material.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
import &#39;dart:collection&#39;;

class TaskStorage with ChangeNotifier {
  List&lt;ModelTask&gt; _myTasks = [];

  void add(ModelTask newTask) {
    _myTasks.add(newTask);
    notifyListeners();
  }

  UnmodifiableListView&lt;ModelTask&gt; get tasks =&gt; UnmodifiableListView&lt;ModelTask&gt;(_myTasks);
}

class ModelTask {
  int someData = 10;
}

class Task extends StatelessWidget {
  final ModelTask task;

  const Task({
    required this.task,
  });

  @override
  Widget build(BuildContext context) {
    return Text(&quot;task: ${task.someData}&quot;);
  }
}

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

  @override
  State createState() =&gt; _TasksScreenState();
}

class _TasksScreenState extends State&lt;TasksScreen&gt; {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: ListView.builder(
            itemBuilder: (context, index) {
              return Task(task: context.watch&lt;TaskStorage&gt;().tasks[index]);
            },
            itemCount: context.watch&lt;TaskStorage&gt;().tasks.length,
          ),
        ),
        ElevatedButton(
          onPressed: () =&gt; context.read&lt;TaskStorage&gt;().add(ModelTask()),
          child: Text(&quot;ADD&quot;),
        ),
      ],
    );
  }
}

void main() =&gt; runApp(
      MaterialApp(
        home: Scaffold(
          backgroundColor: Colors.green,
          appBar: AppBar(),
          body: ChangeNotifierProvider&lt;TaskStorage&gt;(
            create: (BuildContext context) {
              return TaskStorage();
            },
            child: const TasksScreen(),
          ),
        ),
      ),
    );

Maybe this could still help you. I would recommend looking at the documentation of the riverpod package.

huangapple
  • 本文由 发表于 2023年7月11日 03:02:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76656611.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定