まず、Scaffoldはページの土台です。StreamBuilderはWidgetを返しているに過ぎません。必ず、Scaffoldのbody内に設置しましょう。
次に、ConsumerWidget内では状態の変更ができません。
状態を変更する場合はChangeNotifier、つまりuser_model.dart内でappBarTitleを定める必要があります。
その際はvarを使わない方がいいです。理由はappBarTitleの型がdynamicになり、なんでも取れる状態になってしまいます。
この状態は少し危険で、appBarTitleの型がintになってしまっても、エディタの方でエラーが検知できず、実際にアプリをビルドして初めてエラーが起こり、「エディタの方でエラー出てないから、何が悪いのか分からない」という状況におちいってしまいます。
なので変数名の前に型を宣言しましょう。
(ちなみにsnapshot.data!.docs.mapの部分でMapのvalueをdynamicとして宣言しているのは、cloudFirestoreのMapのvalueはdynamicでないといけないという決まりがあるからです。なので、Webアプリを制作している方々はその不安定さが嫌いなので、FirebaseといったNoSQLを使わず、RDBを使っています。モバイルアプリの場合のデータベースはcloudFirestore一択なので、その欠点には目をつぶるしかありません)
あと、watch(UserProvider)をprogramWidgetModelと名付けるのはやめといた方がいいと思います。
program_widget.dartで複数のModelを読み込むことがあるからです。(滅多にないですが)
素直にuserModelでいいと思います。
上記のコードを見る限り質問者さんは一人を取得したいように感じます。
それならリアルタイム取得である必要はなく、一回の取得だけで良いです。
以上のことを考慮すると、コードは以下のようになります。
(FirebaseAuthを利用してのユーザ登録、そしてその際にusersにuidを含むDocumentをcloudFirestoreに追加することは省略しています。)
dart
1// user_model.dart
2import 'package:flutter/material.dart';
3
4import 'package:firebase_auth/firebase_auth.dart';
5import 'package:cloud_firestore/cloud_firestore.dart';
6import 'package:flutter_riverpod/flutter_riverpod.dart';
7
8final userProvider = ChangeNotifierProvider(
9 (ref) => UserModel()
10);
11
12class UserModel extends ChangeNotifier {
13
14 bool isLoading = false;
15 String appBarTitle = '';
16 User? currentUser;
17 late DocumentSnapshot currentUserDoc;
18
19 // watch(userProvider)が呼ばれた時に発火する
20 UserModel() {
21 init();
22 }
23
24 Future init() async {
25 startLoading();
26 setCurrentUser();
27 await setCurrentUserDoc();
28 endLoading();
29 }
30
31 void startLoading() {
32 isLoading = true;
33 // 変更をWidgetに通知する
34 notifyListeners();
35 }
36
37 void setCurrentUser() {
38 currentUser = FirebaseAuth.instance.currentUser;
39 }
40
41 void setAppBarTitle() {
42 appBarTitle = currentUserDoc['name'];
43 }
44 void endLoading() {
45 isLoading = false;
46 // 変更をWidgetに通知する
47 notifyListeners();
48 }
49
50 Future setCurrentUserDoc() async {
51 try{
52 await FirebaseFirestore.instance
53 .collection('users')
54 .where('uid',isEqualTo: currentUser!.uid)
55 .limit(1)
56 .get()
57 .then((querySnapshot) {
58 querySnapshot.docs.forEach((DocumentSnapshot documentSnapshot) {
59 currentUserDoc = documentSnapshot;
60 });
61 });
62 } catch(e) {
63 print(e.toString());
64 }
65 }
66
67}
dart
1// program_widget.dart
2import 'package:flutter/material.dart';
3
4import 'package:flutter_riverpod/flutter_riverpod.dart';
5
6import 'user_model.dart';
7
8class ProgramWidget extends ConsumerWidget {
9
10 const ProgramWidget({Key? key}) : super(key: key);
11 Widget build(BuildContext context, ScopedReader watch) {
12 final userModel = watch(userProvider);
13 return userModel.isLoading ?
14 Text('Loading')
15 : Scaffold(
16 appBar: AppBar(
17 // 普通であればText(userModel.currentUserDoc['name'])で良い
18 title: Text(
19 'ようこそ' + userModel.appBarTitle + 'さん',
20 style: const TextStyle(
21 // 細かいけれどint型ではなく、double型であることがわかるように20ではなく20.0になっています
22 fontSize: 20.0,
23 fontWeight: FontWeight.bold
24 ),
25 ),
26 ),
27 );
28 }
29}